递归+暴力的构造【HT&NFLS-002】T2 三值序列 题解
【HT&NFLS-002】T2 三值序列 题解
题意
输入一个长为 \(n\) 的数列 \(a_{0\sim n-1}\)。
设 \(V=\{0,1,\cdots ,2^n-1\}\)。表示 \(2^n\) 个点的编号集合。
需构造一组完美匹配 \(M\subseteq V\times V\) 使得 \(\forall k\in \N\cap[0,n-1],\) 恰有 \(a_k\) 个匹配对 \((u,v)\) 满足 \(u\oplus v=2^k\)。
\(n\le 20,\sum_{i=0}^{n-1}a_i=2^{n-1}\)
解析
首先特判 \(n=1\)。
首先考虑最高位 \(a_{n-1}\),当 \(k\ne n-1\) 时,每组 \((u,v)\) 的第 \(n-1\) 位要求相同。而 \(k=n-1\) 时要求 \((u,v)\) 的第 \(n-1\) 不相同。所以 \(a_{n-1}\) 必须是偶数。因为如果 \(a_{n-1}\) 是奇数,则会消耗奇数个 \(n-1\) 位是 \(1\) 的数。由于一开始有 \(2^{n-1}\) 即偶数个数,所以这样剩下了奇数个 \(n-1\) 为是 \(1\) 的数,而接下来每次匹配都会消耗 \(0\) 个或 \(2\) 个 \(n-1\) 位是 \(1\) 的数,最后一定会剩下。
而因为每一位等价,任何位都等价于第 \(n-1\) 位,所以 \(\forall i,2|a_i\)。
再考虑规约增量法解决构造题,即先将 \(n\) 的规模缩减为 \(n-1\) 的规模,再通过 \(n-1\) 的规模推到 \(n\) 的规模。
考虑如何将一个第 \(i\) 位的匹配变成第 \(n-1\) 位的匹配,找到匹配 \((x,x+2^{i}),(x+2^{n-1},x+2^i+2^{n-1})\),交换后,就可以得到异或和为 \(2^{n-1}\) 的匹配。
假设 \(\forall i,4|a_i\) 那么递归 \(a_i'=\frac {a_i}2\) 也是一个合法的情况。
对于剩余的 \(a_{n-1}\) 个匹配,直接给 \(a'_{n-2}\) 加上 \(\frac{a_{n-1}}2\),再通过上述的方法即可还原成 \(n-1\) 维的匹配。
假设 \(\exists i,a_i\bmod 4=2\),那么给 \(a_i'\) 加上 \(1\),最后通过上述方法交换一对到 \(n-1\) 即可。
如果 \(\frac{a_{n-1}}2\) 大于 \(a_{i}\bmod 4=2\) 的个数即可规约。每次可以取 \(a_{n-1}\) 为 \(\max a_i\),则当 \(n>6\) 时都可以调整。
当 \(n\le 6\) 时直接暴力即可。
标程
#include<bits/stdc++.h>
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define int long long
#define double long double
#define lop(i,a,b) for(int i=a;i<=b;i++)
#define pol(i,a,b) for(int i=a;i>=b;i--)
#define mset(a,v) memset(a,v,sizeof a)
#define mcpy(a,b) memcpy(a,b,sizeof b)
#undef assert
#define assert(x) if(!(x))exit(1);
#define umap unordered_map
#define pb push_back
#define pc(x) __builtin_popcountll(x)
#define fi first
#define se second
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int> pa;
typedef vector<int> vi;
#define DEBUG
#ifndef DEBUG
#define cerr none
ofstream none("nul");
#endif
const int NN=(1<<20)+5;
int n,f[NN];//f[i]表示i与 i^1<<f[i] 相互配对
void BF(vi a){
int n=a.size();
function<bool(int)> DFS=[&](int x){
if(x==1<<n)return true;
if(f[x]!=-1)return DFS(x+1);
lop(i,0,n-1){
if(!a[i]||f[x^(1<<i)]!=-1)continue;
a[i]--;
f[x]=f[x^(1<<i)]=i;
if(DFS(i+1))return true;
a[i]++;
f[x]=f[x^(1<<i)]=-1;
}
return false;
};
DFS(0);
return;
}
void Construct(vi a){
if(a.size()<=1)return BF(a);
int n=a.size();int x=max_element(a.begin(),a.end())-a.begin();
swap(a[x],a[n-1]);
vi b(n-1),w(n-1);
int cnt=0,m=1<<n-1;
lop(i,0,n-2)if(a[i]>>1&1)w[i]++,cnt++;
if(cnt>a[n-1]/2)return BF(a);
//额外多处理几对
w[n-2]+=a[n-1]/2-cnt;
//递归子问题
lop(i,0,n-2)b[i]=a[i]/2+w[i];
Construct(b);
//通过交换将匹配变为对n-1的贡献
lop(i,0,m-1)if((i>>f[i]&1)&&w[f[i]])w[f[i]]--,f[i]=f[i^(1<<f[i])]=n-1;
//复制翻倍
lop(i,0,m-1)f[i+m]=f[i];
//考虑x,n-1交换的影响
lop(i,0,(1<<n)-1){
if((i>>x&1)^(i>>n-1&1)){
int ri=i^(1<<x)^(1<<n-1);
if(i<ri)swap(f[i],f[ri]);
}
}
lop(i,0,(1<<n)-1)if(f[i]==x||f[i]==n-1)f[i]^=x^(n-1);
return;
}
signed main(){
cin>>n;
if(n==1){
cout<<"Yes\n0 1\n";
return 0;
}
vi a(n);
lop(i,0,n-1)cin>>a[i];
lop(i,0,n-1)if(a[i]&1){
cout<<"No\n";
return 0;
}
cout<<"Yes\n";
mset(f,-1);
Construct(a);
lop(i,0,(1<<n)-1){
if(i<(i^(1<<f[i]))){
cout<<i<<" "<<(i^(1<<f[i]))<<"\n";
}
}
return 0;
}

浙公网安备 33010602011771号