直接按照题目的过程模拟是 O ( n ! ) O(n!) O(n!)的, D P DP DP是 O ( 3 n Σ a i ) O(3^n\Sigma a_i) O(3nΣai)的。
考虑将题目的过程规约到另一个问题。
寻找一组解 b i b_i bi使得 1 = ∑ i a i k − b i 1 = \sum_{i}a_ik^{-b_i} 1=∑iaik−bi。
首先显然任意的一个可以得出1的合并过程都可以推出一组解 b i b_i bi
证明:一定可以通过 b i b_i bi构造出一组解。
先证明一个引理:当数字的数量 n > 1 n>1 n>1时一定有两个不相等的 j , k j,k j,k满足 b k = b j = ( int B = max i b i ) b_k = b_j= (\text{int} \ B = \max_i{b_i}) bk=bj=(int B=maxibi)应该懂我的B什么意思吧
引理的证明: 1 = ∑ i a i k − b i − > k B = ∑ i a i k B − b i 1 = \sum_{i}a_ik^{-b_i} -> k^B = \sum_{i}a_ik^{B-b_i} 1=∑iaik−bi−>kB=∑iaikB−bi
这个等式两边的任意一项都是整数。
而左边被 k k k整除,右边的所有的 b i < B b_i < B bi<B的 a i k B − b i a_ik^{B-b_i} aikB−bi也被 k k k整除
所以右边的所有的 b i = B b_i=B bi=B的 a i k B − b i a_ik^{B-b_i} aikB−bi的和一定被 k k k整除。
也就是说所有的 b i = B b_i=B bi=B的 a i a_i ai的和被 k k k整除。
通过阅读英文版的输入格式我们可以发现不存在 k ∣ a i k|a_i k∣ai。
所以 b i = B b_i=B bi=B的 a i a_i ai至少有两个。
所以对于一组 b i b_i bi,我们取出 b k = b j = B b_k =b_j = B bk=bj=B,
把 a k a_k ak和 a j a_j aj合并得到 a ′ a' a′
发现合并后依然满足不存在 k ∣ a ′ k|a' k∣a′
并且我们可以推出来合并出来的 a ′ a' a′它应该对应的 b ′ b' b′,把它们加入,就得到了一个规模为 n − 1 n-1 n−1的子问题,得到了一个 ∣ b ∣ = n − 1 |b|=n-1 ∣b∣=n−1的 b i b_i bi
而当 n = 1 n=1 n=1的时候我们就得到了 1 1 1。
可以设计 d p s , u = 1 dp_{s,u}=1 dps,u=1表示加入了 s s s集合内的所有 a i a_i ai后可以得到 u = ∑ i a i k − b i u = \sum_{i}a_ik^{-b_i} u=∑iaik−bi
可以简单转移,但是会 T L E TLE TLE,用 b i t s e t bitset bitset优化状态存储和转移即可 A C AC AC.
A C C o d e \rm AC \ Code AC Code
#include<bits/stdc++.h> #define maxn 2005 #define pii pair<int,int> #define mp make_pair using namespace std; int n,k,a[16],lg[1<<16],N,sum; bitset<maxn> dp[1<<16]; int d[16]; priority_queue<pii >q; void dfs(int sta,int s){ if(!sta) return; for(;s*k<=sum && dp[sta][s*k];s*=k) for(int t=sta;t;t-=t&-t) d[lg[t&-t]]++; for(int t=sta;t;t-=t&-t) if(s>=a[lg[t&-t]] && dp[sta-(t&-t)][s-a[lg[t&-t]]]){ dfs(sta-(t&-t),s-a[lg[t&-t]]); return; } } int main(){ scanf("%d%d",&n,&k);N=1<<n; for(int i=0;i<n;i++) scanf("%d",&a[i]),lg[1<<i]=i,sum+=a[i]; dp[0][0] = 1; for(int i=1;i<N;i++){ for(int t=i;t;t-=t&-t){ int v=t&-t , u = lg[v]; dp[i] |= (dp[i-v]<<a[u]); } for(int j=sum/k;j>=1;j--) if(dp[i][j*k]) dp[i][j] = 1; } if(!dp[N-1][1]) puts("NO"); else{ puts("YES"); dfs(N-1,1); for(int i=0;i<n;i++) q.push(mp(d[i],a[i])); for(;q.size() > 1;){ pii u = q.top();q.pop(); pii v = q.top();q.pop(); printf("%d %d\n",u.second,v.second); u.second += v.second; for(;u.second % k == 0;u.second /= k) u.first --; q.push(u); } } }