总结一下最近做的一些题目
这个题很明显是一个压状态然后dp的题,场上也一直在往这个方向想,但是没有做出来。
首先,最暴力的压状态方法就是把最后k位的最小表示全部压下来,然后每次确保转移到的不是回文串即可,复杂度 \(O(nBell_k)\),明显跑不了。
然后场上的想法是类似回文树一样,维护最后k位的fail集合,但是这是每一次跳fail与fail的前一位是什么字符有关,而这是难以转移的,场上经过若干次尝试,发现不可做,就只好打了最低的暴力。
然后题解的做法就十分巧妙了,这种dp的思想以前并没有见过,所以记录下来,希望以后可以变为一个常规的套路。
首先,改变计算答案的方式,之前的做法是保证0每一次最后k位都不回文,但是这样是难以实现的。于是考虑容斥,令 F(x) 为至少有x个长度为k的回文串,那么最终的答案就是 \(\sum F(i) \times-1^i\),于是在dp的时候可以把容斥系数和方案数一起算,每次可以钦定最后k位是回文的,那么容斥系数会取反。
那么现在需要设计dp的状态:每一次转移可以是最后一位随便填,或者强行钦定最后k位是回文。
如果钦定最后k为是回文的,那么就会出现k/2对相等关系(s_1=s_k,s_2=s_k-1,……),于是可以使用令dp状态为最后k位的相等关系的最小表示(如:状态:112331 表示第1、2、6位是相等的,第4、5位是相等的),注意这个状态只记录了钦定的相等关系,即:同一块内必定相等,但是不同的块不一定不相等,所有dp算的是至少出现x次回文的方案数。
然后考虑转移,这是本题第二个之处:对方案的贡献不要在一个字符进来的时候计算,因为一个字符加入状态以后可能会在之后出现一些相等关系,使它不能独立选择;所以我们在每一个块的最后一个统计答案,即如果当前的第k+1个是它的块的最后一个,那么这个块在之后肯定不会再出现,就可以在这里统计贡献。即看当前出状态的那个,如果它是独立的,计算贡献。
然后状态dfs出来发现不是很多,就可以dp了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Add(int &a,int b){a+=b;if(a>=mod)a-=mod;}
inline int mul(int a,int b){return (ll)a*b%mod;}
int n,k,c,ans,tot;
typedef vector<int> V;
map<vector<int>,int> mp;
int id[100];
void trans1(V &v){
int o=0;
vector<int> v1;
for(int i=0;i<k;i++){
if(!id[v[i]])id[v[i]]=++o;
v1.push_back(id[v[i]]-1);
}
for(int i=0;i<k;i++)id[v[i]]=0;
v=v1;
}
int fa[100];
int gf(int x){return x==fa[x]?x:fa[x]=gf(fa[x]);}
void trans2(V &v){
for(int i=0;i<k;i++)fa[i]=i;
for(int i=0;i<k;i++){
int x=gf(v[i]),y=gf(v[k-i-1]);
fa[y]=x;
}
for(int i=0;i<k;i++)v[i]=gf(v[i]);
trans1(v);
}
pair<int,int> to[100005][2];
int sz[100005];
void dfs(V v,int id){
V v1;
bool flag=0;
for(int i=1;i<k;i++)v1.push_back(v[i]),flag|=v[i]==v[0],sz[id]=max(sz[id],v[i]);
sz[id]++;
v1.push_back(k+1);
trans1(v1);
int &now=mp[v1];
if(!now){now=++tot;dfs(v1,now);}
to[id][0]=make_pair(now,flag?1:c);
trans2(v1);
int &now1=mp[v1];
if(!now1){now1=++tot;dfs(v1,now1);}
to[id][1]=make_pair(now1,flag?mod-1:mod-c);
}
int f[2][100005],pw[100];
int main(){
cin>>n>>k>>c;
V v;
for(int i=0;i<k;i++)v.push_back(i);
mp[v]=tot=1,f[0][1]=1,f[0][2]=mod-1;
dfs(v,1);
int p=0;
pw[0]=1;
for(int i=1;i<=k;i++)pw[i]=mul(pw[i-1],c);
for(int i=k+1;i<=n;i++,p^=1){
for(int j=1;j<=tot;j++){
if(!f[p][j])continue;
Add(f[p^1][to[j][0].first],mul(f[p][j],to[j][0].second));
Add(f[p^1][to[j][1].first],mul(f[p][j],to[j][1].second));
f[p][j]=0;
}
}
int ans=0;
for(int i=1;i<=tot;i++)Add(ans,mul(f[p][i],pw[sz[i]]));
cout<<ans<<endl;
return 0;
}
标签:状态,相等,int,1111,回文,dp,mod From: https://www.cnblogs.com/william555/p/16881425.html