1 条题解
-
0
神秘构造题。
观察样例思考容易得到当 中有奇数时无解,当然特判一下 。其它题解都没有说是为什么,其实原因很简单。考虑对于某一位,这一位数字为 的个数和 的个数都是总数的一半。那么每一次用掉一对数字,要么 的个数减 ,要么 的个数减 ,或者两个都减去 。那么由于总数是偶数,第三种情况必须出现偶数次,贡献也就是偶数次了。
考虑如何构造方案。
我们尝试将大小为 的问题变为大小为 的子问题这样递归求解。求出大小为 的某个解后,在回溯的时候通过一些改变变为大小为 的解。
显然将 减 后, 的总和减小一半,并且构造的数的范围也变为 。假设我们已经求出了某个 的解,要将其变换到 的解。我们显然要构造一种方法,使得可以生成第 位为 的数字对。
注意,我们在做大小为 的子问题时,只用了 的数,还有一半的数没有用。思考一下,我们得到一种构造方案:
假设 的子问题中有一对数为 ,并且 ,那么原本我们还可以构造 ,使其异或结果也为 ,这样就给 贡献了 ,我们试图将这 个贡献转移给 。将两对数重新组合一下得到 和 。这样两对数异或结果都是 了。
现在我们完成了回溯要做的事,那么还剩下递归的时候如何把 的贡献分给其他位置。我们知道,分完后 均要减少一半,而减完一半后显然还需要满足这些都是偶数,即用 分完后, 均为 的倍数。也就是说,如果分之前,存在 对 取余为 ,那么必须要由 分两个给 。
显然,如果不够分就完蛋了。但由于位运算,每一位其实是独立的,所以我们可以找一个最大的 与 换一下,并记录下来,回溯的时候把每个数的这两个位置换回来就好了。
这个时候, 的下界就是 ,而最差其他 个数都要分两个走,也就是要分 个。注意到 是指数级别的,增长很快,所以不满足要求的 不会太大, 最大是为 ,可以构造出一组不合法的数据:。
所以很简单,在 的时候直接暴力搜索构造方案即可。
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
- 上传者