CF #805(div3) F - Equate Multisets 贪心|位运算|找规律

 

大意是说给你两个集合a和b,集合大小均为n,不能动a

但可以对b中的数字进行*2,或者/2的操作(次数不限)

问有没有可能把b变成a?

 

俺看到*2和/2,反应是懵逼的..
但知道它们和位运算的对应关系后就有趣很多了~

把一个数字变成二进制形式,*2就是把所有1的位置一起往左移动一位,/2就是往左(算是一个小知识?)

那我们就对a和b都进行二进制拆分~

 

然后我们开始玩一下规律~

b是怎么变成a的?先给结论:当a是b的前缀

比如 b=11011000,a=11,或者a=110,或者a=1101

那么把b>>1若干位就可以得到a了

但你可能举出一个反例:a=1100000,b=1101

这里b往右移动两位得到11,再往左移动五位得到1100000

我们只需要把a的后缀零统统去掉就可以(也就是如果a是2的倍数,就一直/2)

这是因为b可以往左移动任意位数,总能凑出我们需要的任意个零,a的后缀零没有影响

 

好了,那代码怎么实现?

1.先把a的所有后缀零消掉

2.把b顶满(也就是不断<<1,在顶满之前)

 for(int i=1;i<=n;i++) {
            cin>>b[i];
            while(b[i]<=(1ll<<31)) b[i]<<=1;
        }

3.开始对每一个b进行操作>>1,如果能得到a[i],就标记为a[i]被配对了

如果每个a[i]都能被配对,就是有解~

可能有的题解还把b和a Sort了一下,但没必要(俺的代码没排就过了

这是因为如果有解的话,某个b必然对应某个a,在移动的过程中肯定能匹配上的(如果一时没反应过来,这句话多看看>_<)

 

复制代码
#include<bits/stdc++.h>
using namespace std;
long long b[500007],a[500007];
map<long long,long long>mp;
int main()
{
    //freopen("lys.in","r",stdin);
    
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        mp.clear();
        for(int i=1;i<=n;i++) 
        {
            cin>>a[i];
            while(a[i]%2==0) a[i]/=2;
            mp[a[i]]++;
        }
        for(int i=1;i<=n;i++) {
            cin>>b[i];
            while(b[i]<=(1ll<<31)) b[i]<<=1;
        }
        for(int i=1;i<=n;i++)
        {
            while(b[i]){
                b[i]>>=1;
                if(mp[b[i]]) {
                    mp[b[i]]--;
                    break;
                }
            }
        }
        int ok=1;
        for(int i=1;i<=n;i++)
        {
            if(mp[a[i]]) {
                ok=0;break;
            }
        }
        if(ok==1){
            cout<<"YES"<<endl;
        }else {
            cout<<"NO"<<endl;
        }
    }
}
复制代码

 

posted @   liyishui  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示