栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

2021.12.14

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021.12.14

2021.12.14 位运算

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZZvR6qJU-1639543248823)(C:UsersADguyAppDataRoamingTyporatypora-user-images1639452312575.png)]

题意:

输出所有的二元组 < a i , a j > 的个数,使得 a i & a j ≥ a i ⨁ a j a_i & a_j geq a_i bigoplus a_j ai​&aj​≥ai​⨁aj​

思路:

可能这道题对于位运算大师没啥必要写,但我还是想记一下

只有两种情况, a i a_i ai​ 和 a j a_j aj​ 该位上都为 1 1 1 或者都为 0 0 0 才能满足限制条件。

如果均为 0 0 0 ,说明该位不存在,所以这种情况不存在

如果均为 1 1 1 ,说明此时 a i & a j > a i ⨁ a j a_i & a_j > a_i bigoplus a_j ai​&aj​>ai​⨁aj​ ,之后取什么数都没关系了。并且在没有前导 0 0 0 的情况下(说人话就是正常情况),第一位必然均为 1 1 1 。

也就是说,如果两个数转化为二进制后,位数相同,这样首位均为 1 1 1 ,就能满足限制条件。如果位数不同,首位一个为 1 1 1 一个为 0 0 0 。这样第一位就不满足限制条件,直接舍弃。

题目转化为了求位数相同的对的个数。

ll n;
ll a[MAXN];
void solve()
{
    //输入数据
    n=read();
    rep(i,1,n) a[i]=read();
    //求二进制位数
    rep(i,1,n)
    {
        ll tmp=a[i];
        ll cnt=0;
        while(tmp)
        {
            cnt++;
            tmp/=2;
        }
        a[i]=cnt;
    }
    //排序后计算对数
    sort(a+1,a+1+n);
    ll r=1,l=1;
    ll ans=0;
    while(r<=n)
    {
        while(a[r]==a[l]&&r<=n) r++;
        ans=ans+(r-l)*(r-l-1)/2;
        l=r;
    }
    cout< 
组合数+差分维护(数据结构) 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7aGludw-1639543248824)(C:UsersADguyAppDataRoamingTyporatypora-user-images1639453012857.png)]

题意:

一共有 n n n 盏灯。每盏灯 a i a_i ai​ 都有自己的开启时间 l i l_i li​ 和 r i r_i ri​ 。求有多少种方案,使得有 k k k 盏灯正好在某一时刻同时亮着。

思路:

最朴素的暴力。一次次枚举每盏灯的起始时间,在对应区间上加上他的贡献 1 1 1 。如果在某个点 i i i 使得 s u m [ i ] ≥ k sum[i] geq k sum[i]≥k ,那么这盏灯对答案的贡献就累加 C s u m [ i ] − 1 k − 1 C_{sum[i]-1}^{k-1} Csum[i]−1k−1​ 。

可是这样空间需要开 1 e 9 1e9 1e9 ,时间复杂度最坏情况下是 O ( n 2 ) O(n^2) O(n2) ,考虑进行优化。

需要用到区间求和,可以考虑使用差分维护。

差分数组的前缀和就是正常数组的端点值。对于本题,每次 [ l , r ] [l,r] [l,r] 的贡献 + 1 +1 +1 ,在差分中即可表示为 a [ l ] + 1 a[l]+1 a[l]+1 和 a [ r + 1 ] − 1 a[r+1]-1 a[r+1]−1 。因为需要求前缀和,因此必须保证数组是有序的。

这样左端点值为 1 1 1 ,右端点值为 − 1 -1 −1 ,然后将这两个新的二元组push到差分数组中。然后先根据端点的下标进行升序排序,再根据端点的值进行升序排序。(若降序排序会导致重复计算)。这样即可保证前缀和计算正确并且方案数的计算是不重不漏的。

接着扫一遍差分数组。定义一个变量 n o w now now 表示 [ 1 , i ] [1,i] [1,i] 的前缀和。如果当前点权为 1 1 1 表示有一盏新的灯亮起,那么就要计算他对答案的贡献 C n o w k − 1 C_{now}^{k-1} Cnowk−1​ ;如果点权为 0 0 0 ,说明某一盏灯灭了,不统计贡献。接着维护前缀和。

struct node
{
    ll id;
    ll val;
}a[MAXN<<1];//差分

bool cmp(node a,node b)
{
    if(a.id==b.id) return a.val 
差分+思维(这场的出题人是多喜欢差分QAQ) 

阿巴阿巴,是昨天那道求波峰波谷的 $hard $ $version $ 。新增了 q q q 次操作,调换 a [ l ] a[l] a[l] 和 a [ r ] a[r] a[r] 。对于每组样例,输出 q + 1 q+1 q+1 次不同序列的最大战力。

这么看好像我的写法不是歪解,因为这个dp写不了

这样的差分太妙了太妙了太妙了太妙了

继续回到之前波峰波谷的思路上来,其实这就是一个求差分的过程。

以 1 2 5 4 3 6 7 这组样例来进行解释

差分数组 b [ i ] b[i] b[i] 即可表示为 1 1 3 -1 -1 3 4

5-3+7 用差分的形式即可表示为 ∑ i = 1 3 b [ i ] − ∑ i = 1 i = 5 b [ i ] + ∑ i = 1 7 b [ i ] sum_{i=1}^3{b[i]}-sum_{i=1}^{i=5}{b[i]}+sum_{i=1}^7{b[i]} ∑i=13​b[i]−∑i=1i=5​b[i]+∑i=17​b[i]

化简即为 b [ 1 ] + b [ 2 ] + b [ 3 ] + b [ 5 ] + b [ 7 ] b[1]+b[2]+b[3]+b[5]+b[7] b[1]+b[2]+b[3]+b[5]+b[7] ,即减去那些差分数组值为负数的情况。

因为最后一部分必然为波峰,一共有奇数个累加式相加,结合上面的例子应该能想清楚为什么是这个结果吧

如果答案由各段差分数组的值来累加得到,那么调换后就方便维护了。

每次先减去这两个位置上的数在差分意义上对答案的贡献(正数贡献即为本身,负数无贡献);再加上调换位置后两个位置上的数在差分意义上对答案的贡献。

一共是 4 4 4 段,分别为 a [ l ] − a [ l − 1 ] , a [ r ] − a [ r − 1 ] , a [ l + 1 ] − a [ l ] , a [ r + 1 ] − a [ r ] a[l]-a[l-1],a[r]-a[r-1],a[l+1]-a[l],a[r+1]-a[r] a[l]−a[l−1],a[r]−a[r−1],a[l+1]−a[l],a[r+1]−a[r] 。

当 r = l + 1 r=l+1 r=l+1 时, a [ l + 1 ] − a [ l ] a[l+1]-a[l] a[l+1]−a[l] 和 a [ r ] − a [ r − 1 ] a[r]-a[r-1] a[r]−a[r−1] 其实是同一段,会重复计算。其实重复计算也没什么,因为后面也会重复加上他的贡献,但是因为贡献和正负有关,调换位置后正负又会变化,所以必须特判。

ll n,q;
ll a[MAXN];
ll ans;
void init()
{
    ans=0,a[0]=0,a[n+1]=0;
}

void solve()
{
    n=read(),q=read();
    rep(i,1,n) a[i]=read();
    init();
    rep(i,1,n)//以差分的形式计算答案
    {
        ans+=max(a[i]-a[i-1],0ll);
    }
    cout< 
伪博弈(水) 

不放题面,又臭又长。

题意:

给出一个位数为 n n n 的数字。轮到 A l i c e Alice Alice 时标记奇数位上的数字 ,轮到 B o b Bob Bob 时标记偶数位上的数字。这样一直标记持续到还剩最后一个数。如果最后一个数为奇数, A l i c e Alice Alice 获胜;如果是偶数, B o b Bob Bob 获胜。

思路:

如果长度为奇数,最后一个被标记的数必然是奇数位上的数。只要这个未被标记数是奇数, A l i c e Alice Alice 就能获胜。只要 A l i c e Alice Alice 能够保证留下一个奇数, A l i c e Alice Alice 就能获胜。只要奇数位上有一个奇数, A l i c e Alice Alice 就可以把它保留到最后并获胜。

长度为偶数的情况用 B o b Bob Bob 推类似。

char s[MAXN];
void solve()
{
    //Raze 先手 奇数位置 ;Breach 后手 偶数位置
    ll n,tmp;
    n=read();
    ll cnt1=0,cnt2=0;
    cin>>s+1;
    rep(i,1,n)
    {
        tmp=s[i]-'0';
        if((i&1)&&(tmp&1)) cnt1++;
        else if(!(i&1)&&!(tmp&1)) cnt2++;
    }
    int ans;
    if((n&1)&&cnt1>0)
    {
        ans=1;
    }
    else if((n&1)&&cnt1==0)
    {
        ans=2;
    }
    else if(!(n&1)&&cnt2>0)
    {
        ans=2;
    }
    else if(!(n&1)&&cnt2==0)
    {
        ans=1;
    }
    debug(ans);
}
傻逼结论题

题面又臭又长并且题意不清,只放张图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DCjFkoDT-1639543248824)(C:UsersADguyAppDataRoamingTyporatypora-user-images1639469007028.png)]

这样的东西被叫做楼梯。可以看出这个楼梯的大小为 7 7 7 ,由 7 7 7 个正方形组成。

如果一个楼梯的大小为 m m m ,并且正好能由 m m m 个正方形组成,我们称之为完美阶梯。

现给出 n n n 个 大小为 1 × 1 1 times 1 1×1 的正方形,问通过这些正方形最多能够组成多少个 不同 的完美阶梯。

思路:

傻逼结论题。

大小为 1 , 3 , 7 , 15...... 1,3,7,15...... 1,3,7,15...... 的阶梯是构成完美阶梯的充分条件。

可以把一个完美阶梯看成三部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n946BnPY-1639543248825)(C:UsersADguyAppDataRoamingTyporatypora-user-images1639469484935.png)]

设上一块完美阶梯的大小为 k k k 。则根据图显然可以得到当前这块完美阶梯的大小为 2 × k + 1 2times k+1 2×k+1

三部分组成的阶梯数量为 k + k + 1 = 2 × k + 1 k+k+1=2times k+1 k+k+1=2×k+1 ,因此可以证明这是一块完美阶梯。

那么就枚举这些情况并计数就好了。

void solve()
{
    ll n;
    n=read();
    int ans=0;
    for(ll i=1;i*(i+1)/2<=n;i=i<<1|1)
    {
        ans++;
        n-=(i+1)*i/2;
    }
    cout< 

imes k+1$

三部分组成的阶梯数量为 k + k + 1 = 2 × k + 1 k+k+1=2times k+1 k+k+1=2×k+1 ,因此可以证明这是一块完美阶梯。

那么就枚举这些情况并计数就好了。

void solve()
{
    ll n;
    n=read();
    int ans=0;
    for(ll i=1;i*(i+1)/2<=n;i=i<<1|1)
    {
        ans++;
        n-=(i+1)*i/2;
    }
    cout<
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/665533.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号