JZOJ 5711. 【北大夏令营2018模拟5.13】时间幻象
Description
Input
從文件 return.in 中讀入數據。
輸入文件的第1行2個整數n,m,表示時間線的長度和轉移的個數。
后面m行每行2個空格隔開的字符串A? ,B? ,描述一組轉移。
Output
輸出到文件 return.out 中。
輸出文件僅1行,1個非負整數,表示答案。
Sample Input
2 3
A B
A C
D D
Sample Output
5
解釋:一種最優方案:AA → AB → BA → BC → CB。
Data Constraint
Solution
我們發現其實所謂字符串轉換只跟每個字母的個數有關,因為相鄰兩個字母可以交換。
首先我們要意識到一點:狀態數和方案數都不會很多!!!
狀態數只有:C4?130+4?1=C333=5456C30+4?14?1=C333=5456 (用擋板問題解決)。
而答案也不會很多,C1530C3015 也沒多大(字母數總和最多才30),并不會爆 long long 。
對于一個狀態(設其A-D的個數分別是p1,p2,p3,n?p1?p2?p3p1,p2,p3,n?p1?p2?p3),
那么它可以轉換成的不同排列個數就是:
Cp1n?Cp2n?p1?Cp3n?p1?p2?Cn?p1?p2?p3n?p1?p2?p3Cnp1?Cn?p1p2?Cn?p1?p2p3?Cn?p1?p2?p3n?p1?p2?p3我們將這個值設為這個狀態的權值。
接著我們枚舉那 mm 中轉移方式,將每種狀態轉移出去并連邊(反正狀態數又不多)。
做一遍 tarjan 縮環,一個強連通分量的權值就是其中所有點權之和。
這就變成一個點帶權的DAG了,我們需要求一條最長路徑,
那么直接像拓撲排序一樣DP一遍就好了。
時間復雜度約為 O(C333?m)O(C333?m) 。
Code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N=32,M=6000; struct data {int x,y; }edge[M*N]; int n,m,tot,top,num,cnt; LL ans; int first[M],nex[M*N],en[M*N]; int a[N<<1][5],b[N<<1][5],c[5],d[5]; int name[N][N][N],q[M],deg[M]; int dfn[M],low[M],st[M],col[M]; bool bz[M]; LL val[M],val1[M],g[N][N],f[M]; char s[N],t[N]; inline int min(int x,int y) {return x<y?x:y; } inline int get(int *pos) {return name[pos[1]][pos[2]][pos[3]]; } inline LL calc(int *pos) {return g[n][pos[1]]*g[n-pos[1]][pos[2]]*g[n-pos[1]-pos[2]][pos[3]]; } inline void insert(int x,int y) {nex[++tot]=first[x];first[x]=tot;en[tot]=y; } void ergodic(int x,int y) {if(!y){name[c[1]][c[2]][c[3]]=++tot;return;}if(x>4) return;for(int i=0;i<=y;i++){c[x]=i;ergodic(x+1,y-i);c[x]=0;} } void dfs(int x,int y) {if(!y){int now=get(c);val[now]=calc(c);for(int i=1;i<=m;i++){for(int j=1;j<=4;j++) d[j]=c[j]-a[i][j];bool pd=true;for(int j=1;j<=4;j++)if(d[j]<0){pd=false;break;}if(!pd) continue;for(int j=1;j<=4;j++) d[j]+=b[i][j];int to=get(d);if(now==to) continue;edge[++cnt]=(data){now,to};insert(now,to);}return;}if(x>4) return;for(int i=0;i<=y;i++){c[x]=i;dfs(x+1,y-i);c[x]=0;} } void tarjan(int x) {dfn[x]=low[x]=++tot;bz[st[++top]=x]=true;for(int i=first[x];i;i=nex[i])if(!dfn[en[i]]){tarjan(en[i]);low[x]=min(low[x],low[en[i]]);}elseif(bz[en[i]]) low[x]=min(low[x],dfn[en[i]]);if(dfn[x]==low[x]){num++;do{col[st[top]]=num;bz[st[top--]]=false;}while(st[top+1]^x);} } void work() {int l=0,r=0;for(int i=1;i<=num;i++)if(!deg[i]){q[++r]=i;f[i]=val1[i];if(f[i]>ans) ans=f[i];}while(l<r){int x=q[++l];for(int i=first[x];i;i=nex[i])if(f[x]+val1[en[i]]>f[en[i]]){f[en[i]]=f[x]+val1[en[i]];if(f[en[i]]>ans) ans=f[en[i]];q[++r]=en[i];}} } int main() {freopen("return.in","r",stdin);freopen("return.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%s %s",s+1,t+1);int len=strlen(s+1);for(int j=1;j<=len;j++){a[i][s[j]-'A'+1]++;b[i][t[j]-'A'+1]++;}}for(int i=0;i<=n;i++) g[i][0]=1;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++) g[i][j]=g[i-1][j]+g[i-1][j-1];ergodic(1,n);int node=tot;tot=0;dfs(1,n);tot=0;for(int i=1;i<=node;i++)if(!dfn[i]) tarjan(i);for(int i=1;i<=node;i++) val1[col[i]]+=val[i];memset(first,tot=0,sizeof(first));for(int i=1;i<=cnt;i++)if(col[edge[i].x]^col[edge[i].y]){insert(col[edge[i].x],col[edge[i].y]);deg[col[edge[i].y]]++;}work();printf("%lld",ans);return 0; }總結
以上是生活随笔為你收集整理的JZOJ 5711. 【北大夏令营2018模拟5.13】时间幻象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5710. 【北大夏令营201
- 下一篇: JZOJ 5192. 【NOI2017模