bzoj 1093 [ZJOI2007]最大半连通子图——缩点+拓扑
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1093
缩点+拓扑,更新长度的时候维护方案数。
结果没想到处理缩点后的重边,这样的话方案数会算多。
学习人家处理重边的方法好好。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5,M=1e6+5; int n,m,hd[N],rd[N],cd[N],mod,dp[N],f[N],ans,prn,lst[N]; int dfn[N],low[N],tim,cnt,col[N],sta[N],top,h,t,siz[N]; bool ins[N]; struct Ed{ int nxt,fr,to; Ed(int n=0,int f=0,int t=0):nxt(n),fr(f),to(t) {} }ed[M]; int rdn() { int ret=0;char ch=getchar(); while(ch>'9'||ch<'0')ch=getchar(); while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar(); return ret; } void tarjan(int cr) { dfn[cr]=low[cr]=++tim; ins[cr]=1;sta[++top]=cr; for(int i=hd[cr],v;i;i=ed[i].nxt) if(!dfn[v=ed[i].to]) tarjan(v),low[cr]=min(low[cr],low[v]); else if(ins[v])low[cr]=min(low[cr],dfn[v]); if(dfn[cr]==low[cr]) { cnt++; while(sta[top]!=cr) { int k=sta[top--];col[k]=cnt;ins[k]=0;siz[cnt]++; } top--;col[cr]=cnt;ins[cr]=0;siz[cnt]++; } } int main() { n=rdn();m=rdn();mod=rdn(); int x,y; for(int i=1;i<=m;i++) { x=rdn();y=rdn(); ed[i]=Ed(hd[x],x,y);hd[x]=i; } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i); memset(hd,0,sizeof hd); for(int i=1,u,v;i<=m;i++) if((u=col[ed[i].fr])!=(v=col[ed[i].to])) ed[i]=Ed(hd[u],u,v),hd[u]=i,rd[v]++,cd[u]++; h=1;t=0; for(int i=1;i<=n;i++) if(!rd[i])dp[i]=siz[i],f[i]=1,sta[++t]=i; while(h<=t) { int k=sta[h++]; for(int i=hd[k],v;i;i=ed[i].nxt) { rd[v=ed[i].to]--;if(!rd[v])sta[++t]=v; if(lst[v]==k)continue;//会把k的出边全走完,所以不会对v先x再y再x地更新 if(dp[k]+siz[v]>dp[v])dp[v]=dp[k]+siz[v],f[v]=f[k]; else if(dp[k]+siz[v]==dp[v])(f[v]+=f[k])%=mod; lst[v]=k; } } for(int i=1;i<=n;i++)if(!cd[i]) if(dp[i]>ans)ans=dp[i],prn=f[i]; else if(dp[i]==ans)(prn+=f[i])%=mod; printf("%d\n%d\n",ans,prn); return 0; }