[CF1450G]Communism

Communism

题解

状压板子题。

首先观察到字符集数量 ⩽ 20 \leqslant 20 20,很容易考虑到状态dp。
我们定义 d p i dp_{i} dpi表示字符集 i i i在原序列中是否可以 被替换成出现过的其它任意字符。
很容易想到 d p dp dp转移:

  • 对于 x ∈ i x\in i xi,若 d p i − x ⋀ k ⋅ l e n i ⩽ c n t i dp_{i-x}\bigwedge k\cdot len_{i}\leqslant cnt_{i} dpixklenicnti,那么 d p i = 1 dp_{i}=1 dpi=1。相当于将 i − x i-x ix全部替换成 x x x,再替换成其它字符。
  • 对于 S ⊂ i S\subset i Si,若 d p S ⋀ d p i − S dp_{S}\bigwedge dp_{i-S} dpSdpiS,则 d p i = 1 dp_{i}=1 dpi=1。如果两个不相交的字符集都可以替换,我们明显可以将它们换成同一个字符。

但由于我们的第二个转移涉及到子集枚举,时间复杂度达到了 O ( 3 ∣ C ∣ ) O\left(3^{\left|C\right|}\right) O(3C),明显是行不通的。
考虑优化,很明显,对于我们原来的条件 k ⋅ l e n ⩽ c n t k\cdot len\leqslant cnt klencnt,可以转化成 k ⩽ c n t l e n k\leqslant \frac{cnt}{len} klencnt
可以发现,当我们转移的两个字符集越连续时,它们的 c n t l e n \frac{cnt}{len} lencnt越大。
所以我们枚举的两个字符集,越连续时它们越有可能满足 d p S ⋀ d p i − S dp_{S}\bigwedge dp_{i-S} dpSdpiS
所以我们可以先将字符集中字符的顺序按它第一个出现的位置排序,转移就将 i i i分成连续的两段,可以发现,这样明显是更优的。

至于判断 x x x能否作为答案,我们只要看 d p C − x dp_{C-x} dpCx是否可行即可。
很明显,当 d p C − x dp_{C-x} dpCx 1 1 1时,我们就可以将其全部转化为 x x x
所以跑一遍状压即可。

时间复杂度 O ( ∣ C ∣ 2 ∣ C ∣ ) O\left(\left|C\right|2^{\left|C\right|}\right) O(C2C)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 5005
#define MAXM ((1<<20)+5)
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x7f7f7f7f;
const int mo=1e9+7;
const int iv2=5e8+4;
const int jzm=2333;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int n,a,b,tot,L[25],R[25],Lst[MAXM],Rst[MAXM],cnt[MAXM],sum[25],bit[MAXM],idx,id[MAXN];
bool dp[MAXM];char str[MAXN],ans[25];
map<char,int>mp1;map<int,char>mp2;
signed main(){
	read(n);read(a);read(b);scanf("\n%s",str+1);dp[0]=1;
	for(int i=1;i<=n;i++)if(!mp1[str[i]])mp1[str[i]]=++tot,mp2[tot]=str[i];
	for(int i=1;i<=n;i++)id[i]=mp1[str[i]];int lim=(1<<tot)-1;
	for(int i=1;i<=n;i++)R[id[i]]=i;for(int i=n;i>0;i--)L[id[i]]=i,sum[id[i]]++;
	for(int i=1;i<(1<<tot);i++){
		Lst[i]=n;bit[i]=bit[i>>1]+(i&1);
		for(int j=1;j<=tot;j++)
			if(i&(1<<j-1))cnt[i]+=sum[j],
				Lst[i]=min(Lst[i],L[j]),
				Rst[i]=max(Rst[i],R[j]);
	}
	for(int i=1;i<(1<<tot);i++)
		for(int j=1;j<=tot;j++){
			int lim=(1<<j)-1,S1=i&lim,S2=i^S1;
			if(dp[S1]&&dp[S2]){dp[i]=1;break;}
			if(!(i&(1<<j-1)))continue;int S=i^(1<<j-1);
			if(dp[S]&&(Rst[i]-Lst[i]+1)*a<=cnt[i]*b){dp[i]=1;break;}
		} 
	for(int i=1;i<=tot;i++)if(dp[lim^(1<<i-1)])ans[++idx]=mp2[i];
	sort(ans+1,ans+idx+1);printf("%d ",idx);
	for(int i=1;i<=idx;i++)printf("%c ",ans[i]);puts("");
	return 0;
}

谢谢!!!

posted @ 2021-07-13 09:46  StaroForgin  阅读(18)  评论(0)    收藏  举报  来源