[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 x∈i,若 d p i − x ⋀ k ⋅ l e n i ⩽ c n t i dp_{i-x}\bigwedge k\cdot len_{i}\leqslant cnt_{i} dpi−x⋀k⋅leni⩽cnti,那么 d p i = 1 dp_{i}=1 dpi=1。相当于将 i − x i-x i−x全部替换成 x x x,再替换成其它字符。
- 对于 S ⊂ i S\subset i S⊂i,若 d p S ⋀ d p i − S dp_{S}\bigwedge dp_{i-S} dpS⋀dpi−S,则 d p i = 1 dp_{i}=1 dpi=1。如果两个不相交的字符集都可以替换,我们明显可以将它们换成同一个字符。
但由于我们的第二个转移涉及到子集枚举,时间复杂度达到了 
     
      
       
       
         O 
        
        
        
          ( 
         
         
         
           3 
          
          
          
            ∣ 
           
          
            C 
           
          
            ∣ 
           
          
         
        
          ) 
         
        
       
      
        O\left(3^{\left|C\right|}\right) 
       
      
    O(3∣C∣),明显是行不通的。
 考虑优化,很明显,对于我们原来的条件 
     
      
       
       
         k 
        
       
         ⋅ 
        
       
         l 
        
       
         e 
        
       
         n 
        
       
         ⩽ 
        
       
         c 
        
       
         n 
        
       
         t 
        
       
      
        k\cdot len\leqslant cnt 
       
      
    k⋅len⩽cnt,可以转化成 
     
      
       
       
         k 
        
       
         ⩽ 
        
        
         
         
           c 
          
         
           n 
          
         
           t 
          
         
         
         
           l 
          
         
           e 
          
         
           n 
          
         
        
       
      
        k\leqslant \frac{cnt}{len} 
       
      
    k⩽lencnt。
 可以发现,当我们转移的两个字符集越连续时,它们的 
     
      
       
        
         
         
           c 
          
         
           n 
          
         
           t 
          
         
         
         
           l 
          
         
           e 
          
         
           n 
          
         
        
       
      
        \frac{cnt}{len} 
       
      
    lencnt越大。
 所以我们枚举的两个字符集,越连续时它们越有可能满足 
     
      
       
       
         d 
        
        
        
          p 
         
        
          S 
         
        
       
         ⋀ 
        
       
         d 
        
        
        
          p 
         
         
         
           i 
          
         
           − 
          
         
           S 
          
         
        
       
      
        dp_{S}\bigwedge dp_{i-S} 
       
      
    dpS⋀dpi−S。
 所以我们可以先将字符集中字符的顺序按它第一个出现的位置排序,转移就将 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i分成连续的两段,可以发现,这样明显是更优的。
至于判断 
     
      
       
       
         x 
        
       
      
        x 
       
      
    x能否作为答案,我们只要看 
     
      
       
       
         d 
        
        
        
          p 
         
         
         
           C 
          
         
           − 
          
         
           x 
          
         
        
       
      
        dp_{C-x} 
       
      
    dpC−x是否可行即可。
 很明显,当 
     
      
       
       
         d 
        
        
        
          p 
         
         
         
           C 
          
         
           − 
          
         
           x 
          
         
        
       
      
        dp_{C-x} 
       
      
    dpC−x为 
     
      
       
       
         1 
        
       
      
        1 
       
      
    1时,我们就可以将其全部转化为 
     
      
       
       
         x 
        
       
      
        x 
       
      
    x。
 所以跑一遍状压即可。
时间复杂度 O ( ∣ C ∣ 2 ∣ C ∣ ) O\left(\left|C\right|2^{\left|C\right|}\right) O(∣C∣2∣C∣)
源码
#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;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号