Loading

递归+暴力的构造【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;
}

后记

posted @ 2025-07-01 20:35  lupengheyyds  阅读(13)  评论(0)    收藏  举报