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; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)