[NOI.AC#35]string 缩点+拓扑排序

本文介绍了一种处理字符串所有可能排列并将其映射到整数范围的方法,通过使用int128来处理大数运算。文章详细描述了如何根据m种魔法连边,在有向无环图(DAG)上进行动态规划(DP),以解决特定问题。包括了完整的C++代码实现。

链接

因为有交换相邻字母,因此给你字符串就相当于给你了这个字符串的所有排列

把等价的串映射到整数范围,再根据 \(m\) 种魔法连边,缩点后在 DAG 上DP即可

无耻地用了int128

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
typedef __int128 ll;
typedef unsigned int uint;
typedef unsigned long long ull;
template<typename T,typename U>inline char smin(T&x,const U&y){return x>y?x=y,1:0;}
template<typename T,typename U>inline char smax(T&x,const U&y){return x<y?x=y,1:0;}
const int N=52,MN=N*N*N;
int n,m,c1[N<<1][4],c2[N<<1][4],num[N][N][N],T;
ll C[N][N],cnt[MN],val[MN],f[MN],ans;
char s1[N],s2[N];
int head[MN],fr[MN*N],to[MN*N],nxt[MN*N],tot;
inline void add(int x,int y){if(x==y)return;fr[++tot]=x,to[tot]=y,nxt[tot]=head[x];head[x]=tot;}
int s[MN],top,dfn[MN],dfs_clock,bel[MN],scc;
bool ins[MN];
int dfs(int x){
   int low=dfn[x]=++dfs_clock;
   s[++top]=x,ins[x]=1;
   for(int i=head[x];i;i=nxt[i]){
       const int&y=to[i];
       if(!dfn[y])smin(low,dfs(y));
       else if(ins[y])smin(low,dfn[y]);
   }
   if(low==dfn[x]){
       ++scc;
       do bel[s[top]]=scc,val[scc]+=cnt[s[top]],ins[s[top]]=0;while(s[top--]!=x);
   }
   return low;
}
int ind[MN],q[MN];
void toposort(){
   int l=1,r=0,x;
   REP(i,1,scc)if(!ind[i])q[++r]=i;
   while(l<=r){
       x=q[l++];f[x]+=val[x];smax(ans,f[x]);
       for(int i=head[x];i;i=nxt[i]){
           smax(f[to[i]],f[x]);
           if(!--ind[to[i]])q[++r]=to[i];
       }
   }
}
template<typename Tp>inline void print(Tp x){
   static char s[50];
   int top=0;
   for(;x;x/=10)s[++top]=x%10^'0';
   while(top)putchar(s[top--]);
}
int main(){
   scanf("%d%d",&n,&m);
   REP(i,1,m){
       scanf("%s%s",s1+1,s2+1);int len=strlen(s1+1);
       REP(j,1,len)++c1[i][s1[j]-'A'],++c2[i][s2[j]-'A'];
   }
   C[0][0]=1;
   REP(i,1,n){
       C[i][0]=1;
       REP(j,1,i)C[i][j]=C[i-1][j]+C[i-1][j-1];
   }
   REP(i,0,n)REP(j,0,n-i)REP(k,0,n-i-j){
       num[i][j][k]=++T;
       cnt[T]=C[n][i]*C[n-i][j]*C[n-i-j][k];
   }
   REP(a,0,n)REP(b,0,n-a)REP(c,0,n-a-b){
       int d=n-a-b-c;
       REP(i,1,m)if(a>=c1[i][0]&&b>=c1[i][1]&&c>=c1[i][2]&&d>=c1[i][3]){
           int e,f,g,h;
           e=a-c1[i][0]+c2[i][0];
           f=b-c1[i][1]+c2[i][1];
           g=c-c1[i][2]+c2[i][2];
           add(num[a][b][c],num[e][f][g]);
       }
   }
   REP(i,1,T)if(!dfn[i])dfs(i);
   int all=tot;tot=0;memset(head,0,sizeof head);
   REP(i,1,all){
       int&bx=bel[fr[i]],&by=bel[to[i]];
       if(bx!=by)add(bx,by),++ind[by];
   }
   toposort();print(ans);
   return 0;
}

转载于:https://www.cnblogs.com/HolyK/p/9839758.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值