[Codeforces1225G]To Make 1

题意

定义函数

f ( x ) = { f ( x k ) k ∣ x x k ∤ x f(x)= \begin{cases} f(\frac xk)&k\mid x\\ x&k\nmid x \end{cases} f(x)={f(kx)xkxkx

给你 n n n个正整数 ( a 1 , . . . , a n ) (a_1,...,a_n) (a1,...,an)和一个 k k k,每次可以选择两个 x , y x,y x,y删去,并把 f ( x + y ) f(x+y) f(x+y)加入

问最后剩下的一个数是否是 1 1 1,如果是请输出选数的方案

2 ≤ n ≤ 16 , 2 ≤ k ≤ 2000 , ∑ a i ≤ 2000 2\le n\le16,2\le k\le2000,\sum a_i\le2000 2n16,2k2000,ai2000


题解

注意到 n n n范围可以考虑状压 d p dp dp,设 T T T为全集

f s , p f_{s,p} fs,p表示选数集合为 s s s和最后的数为 p p p的情况是否存在

由于 s s s是从小到大枚举的

那么就有 f ( s , p ) = [ ∑ a i ∈ s f ( s − a i , p − a i ) > 1 ] f(s,p)=[\sum_{a_i\in s}f(s-a_i,p-a_i)>1] f(s,p)=[aisf(sai,pai)>1]

特别的如果 f ( s , p × k ) = 1 f(s,p\times k)=1 f(s,p×k)=1,那么 f ( s , p ) = 1 f(s,p)=1 f(s,p)=1

这个转移可以使用可以使用 b i t s e t bitset bitset优化

那么就有( a i a_i ai在状压里表示就是 2 i 2^i 2i

f[s]|=f[s-(1<<i)]<<a[i]

最后只要判断 f ( T , 1 ) f(T,1) f(T,1)是否为 1 1 1即可

这部分的时间复杂度为 O ( 1 ω n 2 n ∑ a i ) O(\frac 1{\omega}n2^n\sum a_i) O(ω1n2nai)

考虑如何使用上述 f f f的值来打印方案

限定一个状态 f ( s , p ) f(s,p) f(s,p)

如果可以就从 f ( s , p k t ) f(s,pk^t) f(s,pkt)转移过来(其中 f ( s , p k t + 1 ) = 0 , t ≥ 1 f(s,pk^{t+1})=0,t\ge1 f(s,pkt+1)=0,t1

否则就从 f ( s − a i , p − a i ) f(s-a_i,p-a_i) f(sai,pai)转移过来(其中 f ( s − a i , p − a i ) = 1 , a i ∈ s f(s-a_i,p-a_i)=1,a_i\in s f(sai,pai)=1,ais

因为 f ( s , p k t ) f(s,pk^t) f(s,pkt)只能从 f ( s − a i , p k t − a i ) f(s-a_i,pk^t-a_i) f(sai,pktai)转移过来,即一定存在一种方案构造出 p k t pk^t pkt

f ( s , p k r ) ( r < t ) f(s,pk^r)(r\lt t) f(s,pkr)(r<t)不能保证一定存在一种选数方案

而题目又说输出任意一种方案即可,所以可以稳妥一点地选择 f ( s , p k t ) f(s,pk^t) f(s,pkt)

那么又如何限制合并的顺序呢?

考虑 f ( s , p ) f(s,p) f(s,p)的是由状态 f ( s ′ , p ′ ) f(s',p') f(s,p)转移过来的(其中 s ′ = s − a i , p ′ = p k t − a i , a i ∈ s s'=s-a_i,p'=pk^t-a_i,a_i\in s s=sai,p=pktai,ais

如果存在 r ≥ 1 r\ge1 r1使得 f ( s ′ , p ′ k r ) = 1 f(s',p'k^r)=1 f(s,pkr)=1那么就一定要先将 s ′ s' s全部合并完了在与 a i a_i ai合并再得出 f ( s , p ) f(s,p) f(s,p)的状态,也就是 s ′ s' s的里数要比 a i a_i ai优先选择(理由和上面类似)

那么可以每次从 f ( s , p ) f(s,p) f(s,p) f ( s , p k t ) f(s,pk^t) f(s,pkt)时给 s s s里的数的优先级全加上 t t t

最后优先级越高的越先合并,合并完得到的新数的优先级和取出来的两个数中的优先级最高的数相同

但是如果新数能被 k k k整除,那么优先级就要相应地下降

这部分的时间复杂度是 O ( n log ⁡ n + ∑ a i k ) O(n\log n+\frac{\sum a_i}k) O(nlogn+kai)

总时间复杂度 O ( 1 ω n 2 n ∑ a i ) O(\frac 1{\omega}n2^n\sum a_i) O(ω1n2nai)

#include<bits/stdc++.h>
using namespace std;
const int N=2005,M=1<<16;
using arr=int[M];
#define Pii pair<int,int>
bitset<N>f[M];
priority_queue<Pii >q;
int n,k,T,Sum,a[20],Rank[20];
void dfs(int S,int p){
	if(!S)return;
	int r=0;
	for(;p*k<=Sum&&f[S][p*k];p*=k,++r);
	for(int i=0;i<n;++i)
		if(S&(1<<i))
			Rank[i]+=r;
	for(int i=0;i<n;++i)
		if(S&(1<<i)&&p>=a[i]&&f[S-(1<<i)][p-a[i]])
			return dfs(S-(1<<i),p-a[i]);
}
int main(){
	scanf("%d%d",&n,&k);
	T=(1<<n)-1;f[0][0]=1;
	for(int i=0;i<n;++i)
		scanf("%d",a+i),Sum+=a[i];
	for(int s=1;s<=T;++s){
		for(int i=0;i<n;++i)if(s&(1<<i))
			f[s]|=f[s-(1<<i)]<<a[i];
		for(int j=Sum/k;j;--j)
			if(f[s][j*k])
				f[s][j]=1;
	}
	if(!f[T][1])return puts("NO"),0;
	puts("YES");
	dfs(T,1);
	for(int i=0;i<n;++i)
		q.push(make_pair(Rank[i],a[i]));
	#define sum second
	#define rank first
	while(q.size()>1){
		Pii u=q.top();q.pop();
		Pii v=q.top();q.pop();
		printf("%d %d\n",u.sum,v.sum);
		for(u.sum+=v.sum;u.sum%k==0;u.sum/=k)
			--u.rank;
		q.push(u);
	}
	#undef sum
	#undef rank
return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值