[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号