YZOJ P4259 [FJWC 2019] 不同的缩写
时间限制:1000MS 内存限制:262144KB
出题人:E.Space
难度:\(6.1\)
-
题目描述
在这个游戏中一共有 \(n\) 个角色。你需要编写一些关于这些角色的对话内容。
你打算用角色名字的一个非空子序列来作为它的简称。
当然,不同的角色要用不同的字符串作为简称,否则你就变量重名了。
你想确定一个简称的分配方案使得所有角色中最长的简称尽量短。
-
输入格式
第一行一个正整数 \(n\)。
接下来 \(n\) 行,每行一个由小写字母组成的字符串,代表一个角色的名字。
不同的角色可能会有相同的名字。
-
输出格式
如果不存在一种分配简称的方案满足条件,输出 \(-1\)。
否则第一行输出一个正整数,表示最长的简称的最小长度。
接下来 \(n\) 行每行一个字符串,按顺序表示每个角色的简称。
若有多种方案满足条件,那么你可以输出任意一种。
-
样例输入
1 2 3 4 5 6 7 8 9 10 11 12 |
11 night nealchen beimingyouyu echo rankinf dntcrybecthlev lagoon cyc alphagem leehwincing clin |
-
样例输出
1 2 3 4 5 6 7 8 9 10 11 12 |
1 g a m e r b o y l w c |
-
数据规模与约定
保证 \(n \leq 300\) ,每个名字的长度不超过 \(300\)。
我校校队成员(gamerboy
首先要求的是长度最小,可以想到二分答案,因为长度比答案大的一定可以,比答案小的一定不行。
因为每个人都与缩写一 一对应,所以其实发现这是一个二分图匹配,用序列自动机爆搜出所有的子序列,使用Trie树判重,然后连边匹配。
这样点/边数很多,所以考虑 Hall 定理,那么大于 \(n\) 个对应字符串的子序列就没有继续搜索的必要了。
这样就有一个点数 \(O(n^2)\) 边数 \(O(n^2)\) 的图,跑 Dinic 的复杂度是 \(O(n^3)\),总复杂度 \(O(n^3logn)\) 。
都知道 Dinic 跑不满,所以这题可以过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #define INFINITE 0x7F7F7F7F #define _min(_a_,_b_) ((_a_)<(_b_)?(_a_):(_b_)) #define _max(_a_,_b_) ((_a_)>(_b_)?(_a_):(_b_)) #define GetReverse(_x) ((_x)&1 ? (_x)+1 : (_x)-1) int gcnt,ghead[105050],gnext[1505050],gnode[1505050],gflow[1505050]; inline void insertLine(register int s,register int t,const int&v=1) { gnext[++gcnt]=ghead[s],ghead[s]=gcnt,gnode[gcnt]=t,gflow[gcnt]=v; gnext[++gcnt]=ghead[t],ghead[t]=gcnt,gnode[gcnt]=s,gflow[gcnt]=0; } int N; int _i,pos[355][355][26]; int tmcnt,cnt,son[105050][26]; void DFSx(register int step,register int x,register int now) { if(step<=0 || tmcnt<=0) return; for(register int j=0;j<26;j++) if(pos[_i][x][j]) { if(!son[now][j]) son[now][j]=++cnt; insertLine(_i,son[now][j]); tmcnt--,DFSx(step-1,pos[_i][x][j],son[now][j]); } } int S,T; int dist[105050]; inline bool BFS() { static int lst[105050]; register int front=0,rear=0; memset(dist,0,sizeof(dist)); dist[S]=1,lst[rear++]=S; while(front<rear) { register int now=lst[front++]; for(register int j=ghead[now],t;j;j=gnext[j]) if(gflow[j]>0 && !dist[t=gnode[j]]) dist[t]=dist[now]+1,lst[rear++]=t; } return dist[T]; } int cur[105050]; int DFS(register int now,register int mxflow) { if(now == T) return mxflow; for(register int j=cur[now],t;j;j=gnext[j]) { cur[now]=j,t=gnode[j]; if(!(gflow[j]>0 && dist[t]==dist[now]+1)) continue; register int nf=DFS(t,_min(mxflow,gflow[j])); if(nf) { gflow[j]-=nf; gflow[GetReverse(j)]+=nf; return nf; } } return 0; } inline int Dinic() { register int ans=0; while(BFS()) { for(register int i=1;i<=T;i++) cur[i]=ghead[i]; while(int nf=DFS(S,INFINITE)) ans+=nf; } return ans; } inline bool CheckValid(register int len) { gcnt=0,memset(ghead,0,sizeof(ghead)); memset(son,0,sizeof(son)); cnt=N; for(register int i=1;i<=N;i++) _i=i,tmcnt=N,DFSx(len,0,1); S=cnt+1,T=S+1; for(register int i=1;i<=N;i++) insertLine(S,i); for(register int i=N+1;i<=cnt;i++) insertLine(i,T); return Dinic()==N; } int nodemap[105050],top; char stk[105050],ans[355][105050]; void DFSt(register int now) { if(nodemap[now]) memcpy(ans[nodemap[now]],stk,sizeof(stk)); for(register int j=0;j<26;j++) if(son[now][j]) stk[++top]=j+'a',DFSt(son[now][j]),stk[top--]=0; } char s[105050]; int main() { scanf("%d",&N); register int mxls=0; for(register int lp=1;lp<=N;lp++) { scanf("%s",&s[1]); register int ls=strlen(&s[1]); for(register int i=ls-1;i>=0;i--) { for(register int j=0;j<26;j++) pos[lp][i][j]=pos[lp][i+1][j]; pos[lp][i][s[i+1]-'a']=i+1; } mxls=_max(mxls,ls); } register int left=1,right=mxls; while(left<=right) { register int mid=(left+right)>>1; if(CheckValid(mid)) right=mid-1; else left=mid+1; } if(right+1>mxls) { printf("%d\n",-1); return 0; } printf("%d\n",right+1); CheckValid(right+1); for(register int i=1;i<=N;i++) for(register int j=ghead[i];j;j=gnext[j]) if(!gflow[j]) { nodemap[gnode[j]]=i; break; } DFSt(1); for(register int i=1;i<=N;i++) printf("%s\n",&ans[i][1]); return 0; } |