Codeforces 1225G - To Make 1(bitset+状压 dp+找性质)

Codeforces 题目传送门 & 洛谷题目传送门

还是做题做太少了啊……碰到这种题一点感觉都没有……

首先我们来证明一件事情,那就是存在一种合并方式 \(\Leftrightarrow\) \(\exist b_i\in\mathbb{Z}^+,\sum\limits_{i=1}^na_ik^{-b_i}=1\)

考虑充分性,倘若我们已经知道了 \(b_1,b_2,\dots,b_n\) 的值怎样构造合并的序列,考虑 \(B=\max\limits_{i=1}^nb_i\),这里有一个结论,那就是 \(b_i=B\)\(i\) 至少有两个,否则,等式两边同时乘 \(k^B\) 可得 \(\sum\limits_{i=1}^na_ik^{B-b_i}=k^B\),而右边 \(k\mid k^B\),左边 \(\forall b_i<B,k\mid k^{B-b_i}\),又 \(k\nmid a_i\),故若满足 \(b_i=B\)\(i\) 只有一个,那么 \(\sum\limits_{i=1}^na_ik^{B-b_i}\equiv a_i\not\equiv 0\pmod{k}\),矛盾。

此时假设 \(b_i=b_j=B\),那么我们合并 \(a_i,a_j\) 即可,假设 \(a_i+a_j=A\times k^{\alpha}\),那么 \(a_i,a_j\) 合并后就会变成 \(A\),而合并之后满足 \(\sum\limits_{i}a_ik^{-b_i}\)\(b_i\) 依旧存在,因为 \(\dfrac{a_i}{k^B}+\dfrac{a_j}{k^B}=\dfrac{a_i+a_j}{k^B}=\dfrac{A}{k^{B-\alpha}}\),此时只需令其它 \(a_i\)\(b_i\) 保持不变,新合并出的元素的 \(b\) 值为 \(B-\alpha\) 即可。如此合并 \(n-1\) 轮必定会得到一个数,这个数就是 \(1\)

接下来考虑必要性,证明必要性等价于证明如果存在合法的合并方案,那么一定可以构造出合法的 \(b\) 序列。显然对于任意一种合并的局面,序列中每个数都可以看作原序列中一些数合并的结果,我们对于第 \(i\) 个数记这个集合为 \(S_i\),显然初始 \(S_i=\{i\}\)。我们构造初始 \(b_i=0\)。我们假设当前步合并了 \(a'_i,a'_j\) 两个元素,并且 \(a'_i+a'_j=A\times k^{\alpha}\),那么我们令 \(S_i,S_j\) 中所有元素的 \(b\) 值都增加 \(\alpha\)。这样构造出来有一个结论,那就是对于任意一个局面中的任意一个数 \(a'_i\),都有 \(a'_i=\sum\limits_{j\in S_i}a_jk^{-b_j}\),其中 \(a_j\) 为初始序列中下标为 \(j\) 的元素的值。这个结论显然可以归纳证明,对于初始局面显然满足,当我们合并两个数 \(a'_i,a'_j\) 时,\(A=\dfrac{a'_i+a'_j}{k^{\alpha}}=\dfrac{\sum\limits_{p\in S_i}a_pk^{-b'_p}+\sum\limits_{p\in S_j}a_pk^{-b'_p}}{k^{\alpha}}=\sum\limits_{p\in S_i}a_pk^{-b'_p-\alpha}+\sum\limits_{p\in S_j}a_pk^{-b'_p-\alpha}=\sum\limits_{p\in S_i\lor p\in S_j}a_pk^{-b_p}\),得证。又因为最终合并出来的数为 \(1\),最后合并出来的数对应的 \(S\) 显然为全集,故 \(\sum\limits_{i=1}^na_ik^{-b_i}=1\)


这样就可以 \(dp\),此题 \(n\) 数据范围很小,故考虑状压 \(dp\)\(dp_{S,x}\) 表示考虑了 \(S\) 中的数,这些数和为 \(x\) 是否可行。转移比较容易,如果不能一眼看出转移的建议康下这个题。转移到 \(dp_{S,x}\) 可以新加入一个元素,即 \(dp_{S,x}|=dp_{S-\{u\},x-a_u}\),或者将序列中所有数都除以 \(k\),即 \(dp_{S,x}|=dp_{S,xk}\),复杂度 \(n2^n\sum a_i\approx 2\times 10^9\),会 TLE。不过注意到 \(dp\) 的每一个元素都是一个 bool 类型的数,故考虑 bitset 优化,复杂度可降到 \(\dfrac{n2^n\sum a_i}{\omega}+2^n\sum a_i\) 即可通过此题。

至于输出方案,就暴力找出 \(b\) 序列然后按照上述方式合并即可。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=16;
const int MAXV=2000;
const int MAXP=1<<MAXN;
int n,k,a[MAXN+5],b[MAXN+5],sum=0;
bitset<MAXV+5> bt[MAXP+5];
void getstate(int st,int x){
//	printf("%d %d\n",st,x);
	if(!st) return;
	if(x*k<=sum&&bt[st][x*k]){
		for(int i=1;i<=n;i++) if(st>>i-1&1) b[i]++;
		getstate(st,x*k);return;
	}
	for(int i=1;i<=n;i++) if(st>>i-1&1){
		if(x-a[i]>=0&&bt[st^1<<i-1][x-a[i]]){
			getstate(st^1<<i-1,x-a[i]);return;
		}
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
	bt[0][0]=1;
	for(int i=0;i<(1<<n);i++){
		for(int j=1;j<=n;j++) if(i>>j-1&1) bt[i]|=bt[i^1<<j-1]<<a[j];
		for(int j=sum/k;j;j--) if(bt[i][j*k]) bt[i][j]=1;
	}
	if(!bt[(1<<n)-1][1]) puts("NO");
	else{
		puts("YES");getstate((1<<n)-1,1);
		priority_queue<pii> q;
		for(int i=1;i<=n;i++) q.push(mp(b[i],a[i]));
		while(q.size()>1){
			pii p1=q.top();q.pop();
			pii p2=q.top();q.pop();
			printf("%d %d\n",p1.se,p2.se);
			p1.se+=p2.se;
			while(p1.se%k==0) p1.se/=k,p1.fi--;
			q.push(p1);
		}
	}
	return 0;
}
posted @ 2021-02-28 09:54  tzc_wk  阅读(88)  评论(0)    收藏  举报