由于d很小,所以可以把每个点拆成d个点,然后对于边(x,y),连边时连接((x,i),(y,i+1))及((x,d),(y,1))。然后可以对这样连的边跑一遍tarjan缩点。然后直接暴力DP即可。不过当时比赛时不知道为什么一直写挂然后掉分了,后来发现用vector特别占用内存,要改成邻接表写。
#include<bits/stdc++.h> using namespace std; const int N=1e5+7; struct edge{int v,nxt;}e1[N*50],e2[N*50]; int n,m,d,tot,top,scc,cnt,hd1[N*50],hd2[N*50],val[N*50],bel[N*50],dfn[N*50],low[N*50],has[N*50],q[N*50],f[N*50]; char str[60]; void add1(int x,int y){e1[++cnt]=(edge){y,hd1[x]},hd1[x]=cnt;} void add2(int x,int y){e2[++cnt]=(edge){y,hd2[x]},hd2[x]=cnt;} void tarjan(int u) { dfn[u]=low[u]=++tot; q[++top]=u; for(int i=hd1[u];i;i=e1[i].nxt) if(!dfn[e1[i].v])tarjan(e1[i].v),low[u]=min(low[u],low[e1[i].v]); else if(!bel[e1[i].v])low[u]=min(low[u],dfn[e1[i].v]); if(low[u]==dfn[u]) { scc++; int now=0; while(now!=u)now=q[top--],bel[now]=scc; } } int dfs(int u) { if(f[u]!=-1)return f[u]; f[u]=0; for(int i=hd2[u];i;i=e2[i].nxt)f[u]=max(f[u],dfs(e2[i].v)); return f[u]+=val[u]; } int main() { scanf("%d%d%d",&n,&m,&d); for(int i=1,x,y;i<=m;i++) { scanf("%d%d",&x,&y); for(int j=1;j<d;j++)add1(x+(j-1)*n,y+j*n); add1(x+(d-1)*n,y); } for(int i=1;i<=n*d;i++)if(!dfn[i])tarjan(i); cnt=0; for(int i=1;i<=n*d;i++) for(int j=hd1[i];j;j=e1[j].nxt) if(bel[i]!=bel[e1[j].v])add2(bel[i],bel[e1[j].v]); for(int i=1;i<=n;i++) { scanf("%s",str+1); for(int j=1;j<=d;j++) if(str[j]=='1'&&has[bel[i+(j-1)*n]]!=i) has[bel[i+(j-1)*n]]=i,val[bel[i+(j-1)*n]]++; } memset(f,-1,sizeof f); printf("%d",dfs(bel[1])); }