• 【CodeForces】925 C.Big Secret 异或


    【题目】C.Big Secret
    【题意】给定数组b,求重排列b数组使其前缀异或和数组a单调递增。(n leq 10^5,1 leq b_i leq 2^{60})
    【算法】异或
    为了拆位分析,先考虑一个简单的问题:已知一个合法b数组和一个数字"1",求数字”1“是否能插入?
    容易发现,”1“插入的位置必须满足前面的数字中有偶数个奇数(可以是0个),因为这样前缀和才能比上一位多1,满足要求。

    进一步的,已知一个有y个奇数的合法b数组和x个数字”1“,求数字”1“能否全部插入?
    利用上面的结论,容易发现当x<=y+1时,只需要在最前面放一个”1“,然后每隔一个奇数放一个”1“就能满足要求(”1“本身也是奇数)。
    而当x>y+1时,无解。

    上面这个结论可以扩展,对于第k位,已知有y个第k位为0的数字的合法b数组,和x个第k位为1的在([1,2^{k+1}-1])范围内的数字,求数字能否全部插入?
    为什么这样扩展是正确的?一个数字能否插入其实只取决于它的最高的1。因为如果最高位异或变成0,无论低位如何都不合法。如果最高为异或变成1,无论低位如何都合法。

    现在考虑做法,如何在插入时保证不跳过每次前缀和为偶数的情况?预处理v[i]表示最高的”1“在第i位的数字列表,从前往后依次确定答案,从低位到高位枚举合法的位数并且数字列表不空就插入,累加前缀和。从低到高枚举就能保证插入的数字的低位一定都是不能插入的,免得低位的”1“使得跳过了前缀和为偶数的情况。

    复杂度(O(60*n))
    本题的关键在于从将数字插入已有的合法数列的角度考虑。

    #include<cstdio>
    #include<vector>
    #define ll long long
    using namespace std;
    vector<ll>v[70];
    int n;ll ans[100010],cur=0;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		ll u;scanf("%lld",&u);
    		for(int j=60;j>=0;j--)if((u>>j)&1){v[j].push_back(u);break;}
    	}
    	for(int i=1;i<=n;i++){
    		bool ok=0;
    		for(int j=0;j<=60;j++)if(!(cur&(1ll<<j))&&!v[j].empty()){
    			cur^=(ans[i]=v[j].back());v[j].pop_back();ok=1;break;
    		}
    		if(!ok){puts("No");return 0;}
    	}
    	for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
    	return 0;
    }
  • 相关阅读:
    MacPE+WinPE-黑苹果之路
    项目已被os x使用 不能打开-黑苹果之路
    X230上安装Yosemite/Win7-黑苹果之路
    SVN-服务器搭建、apache2整合、eclipse使用
    Android开发-Hello World+phonegap(Cordova)
    IOS开发-phonegap上的数据库
    lenovo X230热键功能
    XBox360-双光盘游戏自制GOD
    Kindle3与亚马逊
    iOS开发tips-UIScrollView的Autlayout布局
  • 原文地址:https://www.cnblogs.com/onioncyc/p/9058173.html
Copyright © 2020-2023  润新知