1 条题解

  • 0
    @ 2026-5-7 19:38:20

    神秘构造题。

    观察样例思考容易得到当 aia_i 中有奇数时无解,当然特判一下 n=1n=1。其它题解都没有说是为什么,其实原因很简单。考虑对于某一位,这一位数字为 11 的个数和 00 的个数都是总数的一半。那么每一次用掉一对数字,要么 00 的个数减 22,要么 11 的个数减 22,或者两个都减去 11。那么由于总数是偶数,第三种情况必须出现偶数次,贡献也就是偶数次了。

    考虑如何构造方案。

    我们尝试将大小为 nn 的问题变为大小为 n1n-1 的子问题这样递归求解。求出大小为 n1n-1 的某个解后,在回溯的时候通过一些改变变为大小为 nn 的解。

    显然将 nn11 后,aia_i 的总和减小一半,并且构造的数的范围也变为 02n110\sim 2^{n-1}-1。假设我们已经求出了某个 n1n-1 的解,要将其变换到 nn 的解。我们显然要构造一种方法,使得可以生成第 n1n-1 位为 11 的数字对。

    注意,我们在做大小为 n1n-1 的子问题时,只用了 02n110\sim 2^{n-1}-1 的数,还有一半的数没有用。思考一下,我们得到一种构造方案:

    假设 n1n-1 的子问题中有一对数为 (x,y)(x,y),并且 xy=2kx\oplus y=2^k,那么原本我们还可以构造 (x+2n1,y+2n1)(x+2^{n-1},y+2^{n-1}),使其异或结果也为 2k2^k,这样就给 aka_k 贡献了 22,我们试图将这 22 个贡献转移给 an1a_{n-1}。将两对数重新组合一下得到 (x,x+2n1)(x,x+2^{n-1})(y,y+2n1)(y,y+2^{n-1})。这样两对数异或结果都是 2n12^{n-1} 了。

    现在我们完成了回溯要做的事,那么还剩下递归的时候如何把 an1a_{n-1} 的贡献分给其他位置。我们知道,分完后 a0an2a_0\sim a_{n-2} 均要减少一半,而减完一半后显然还需要满足这些都是偶数,即用 an1a_{n-1} 分完后,a0an2a_0\sim a_{n-2} 均为 44 的倍数。也就是说,如果分之前,存在 aia_i44 取余为 22,那么必须要由 an1a_{n-1} 分两个给 aia_i

    显然,如果不够分就完蛋了。但由于位运算,每一位其实是独立的,所以我们可以找一个最大的 aia_ian1a_{n-1} 换一下,并记录下来,回溯的时候把每个数的这两个位置换回来就好了。

    这个时候,an1a_{n-1} 的下界就是 2n1n\frac{2^{n-1}}{n},而最差其他 n1n-1 个数都要分两个走,也就是要分 2(n1)2(n-1) 个。注意到 an1a_{n-1} 是指数级别的,增长很快,所以不满足要求的 nn 不会太大,nn 最大是为 66,可以构造出一组不合法的数据:{2,6,6,6,6,6}\{2,6,6,6,6,6\}

    所以很简单,在 n6n\le 6 的时候直接暴力搜索构造方案即可。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> p;
    int id[25];
    bool flag[70],ok;
    set<p> s,tmps;
    void dfs(int n,int k,vector<int> a){
    	if(k==(1<<n)){
    		ok=1;
    		return;
    	}
    	if(flag[k]){
    		dfs(n,k+1,a);
    		return;
    	}
    	for(int j=0;j<n;j++){
    		if(a[j]&&!flag[k^(1<<j)]){
    			a[j]--;
    			flag[k]=flag[k^(1<<j)]=1;
    			s.insert({k,k^(1<<j)});
    			dfs(n,k+1,a);
    			if(ok) return;
    			a[j]++;
    			flag[k]=flag[k^(1<<j)]=0;
    			s.erase({k,k^(1<<j)});
    		}
    	}
    }
    void work(int n,vector<int> a){
    	if(n<=6){
    		dfs(n,0,a);
    		return;
    	}
    	int mx=0,mxid;
    	for(int i=0;i<n;i++){
    		if(a[i]>=mx){
    			mx=a[i];
    			mxid=i;
    		}
    	}
    	swap(a[mxid],a[n-1]);
    	int tmpf[25]={0};
    	for(int i=0;i<n-1;i++){
    		if((a[i]>>1)&1){
    			tmpf[i]=1;
    			a[i]+=2;
    			a[n-1]-=2;
    		}
    	}
    	a[n-2]+=a[n-1];
    	tmpf[n-2]+=a[n-1]>>1;
    	for(int i=0;i<n-1;i++){
    		a[i]>>=1;
    	}
    	work(n-1,a);
    	for(auto it=s.begin();it!=s.end();){
    		int x=it->first,y=it->second;
    		int w=__lg(x^y);
    		if(tmpf[w]){
    			tmpf[w]--;
    			it=s.erase(it);
    			s.insert({x,x^(1<<n-1)});
    			s.insert({y,y^(1<<n-1)});
    		}
    		else{
    			if(x<(1<<n-1)&&(y<(1<<n-1))){
    				s.insert({x^(1<<n-1),y^(1<<n-1)});
    			}
    			it++;
    		}
    	}
    	tmps.clear();
    	for(auto it=s.begin();it!=s.end();it++){
    		int x=it->first,y=it->second;
    		x=x^(x&(1<<n-1))^(x&(1<<mxid))^((x>>n-1&1)<<mxid)^((x>>mxid&1)<<n-1);
    		y=y^(y&(1<<n-1))^(y&(1<<mxid))^((y>>n-1&1)<<mxid)^((y>>mxid&1)<<n-1);
    		tmps.insert({x,y});
    	}
    	s=tmps;
    }
    int main(){
    	int n;
    	vector<int> a;
    	cin>>n;
    	if(n==1){
    		puts("0 1");
    		return 0;
    	}
    	a.resize(n+1);
    	for(int i=0;i<n;i++){
    		cin>>a[i];
    		if(a[i]&1){
    			cout<<-1;
    			return 0;
    		}
    	}
    	work(n,a);
    	for(auto x:s){
    		printf("%d %d\n",x.first,x.second);
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    9611
    时间
    1000ms
    内存
    512MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者