因为我数组开错了调了很久。。
按照题意来说很明显的是如果是一个环的话它肯定符合条件,而且为了满足最大的条件,环里的点一定是一起取的,所以先用tarjan缩点。
缩点后实质上就是取一条最长链。
然后很明显就可以按照拓扑序来dp了,side[i]表示到当前这个点为止,链的最长的长度是多少?dp[i]表示如果取最长链的话有几种可能性。
然后转移就是,
if (side[i]==side[j]+size[i]) dp[i]+=dp[j];
else if (side[i]<side[j]+size[i]) dp[i]=dp[j];
size[i]表示i这个点在未缩点之前有多少个点,j到i有一条边。
还有一个点就是,我们取的半联通子图是点集,所以要去掉重边,才能让上述dp有效。
#include<iostream> #include<vector> #include<algorithm> #include<cstdio> #include<queue> #include<stack> #include<cstdlib> #define maxn 200009 #define maxm 2000009 using namespace std; typedef long long ll; struct ding2{ int lef,righ; }bia[maxm]; struct ding{ int to,next; }edge[maxm]; queue<int>q; int num,n,m,ans; int head[maxn],ru[maxn],side[maxn],size[maxn]; ll mo,dp[maxn],ans2; int cnt; void add(int u,int v) { edge[++cnt].to=v;edge[cnt].next=head[u];head[u]=cnt; } void tuopu() { while (!q.empty()) { int now=q.front();q.pop(); ans=max(ans,side[now]); for (int i=head[now];i;i=edge[i].next) { int y=edge[i].to; ru[y]--; if (side[y]<size[y]+side[now]) { side[y]=size[y]+side[now]; dp[y]=dp[now]; } else if (side[y]==size[y]+side[now]) { dp[y]=(dp[y]+dp[now])%mo; } if (ru[y]==0) q.push(y); } } } stack<int>s; int low[maxn],nod[maxn],dfn[maxn],p=0; vector<int>g[maxn]; bool in[maxn]; void tarjan(int x) { dfn[x]=low[x]=++p; s.push(x);in[x]=true; for (int i=0;i<g[x].size();i++) { int y=g[x][i]; if (!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]); else if (in[y]) low[x]=min(low[x],dfn[y]); } if (low[x]==dfn[x]) { ++num; while (s.top()!=x) { nod[s.top()]=num; size[num]++; in[s.top()]=false;s.pop(); } nod[s.top()]=num;size[num]++;in[s.top()]=false;s.pop(); } } bool cmp(ding2 t1,ding2 t2) { return (t1.lef==t2.lef?t1.righ<t2.righ:t1.lef<t2.lef); } int main() { //freopen("semi5.in","r",stdin); //freopen("haha.out","w",stdout); scanf("%d%d%lld",&n,&m,&mo); int x,y; for (int i=1;i<=m;i++) { scanf("%d%d",&x,&y); bia[i].lef=x;bia[i].righ=y; g[x].push_back(y); } num=0;
//缩点 for (int i=1;i<=n;i++) if (dfn[i]==0) tarjan(i); for (int i=1;i<=m;i++) { bia[i].lef=nod[bia[i].lef]; bia[i].righ=nod[bia[i].righ]; } sort(bia+1,bia+1+m,cmp);
//去重边 int lastx=0,lasty=0; for (int i=1;i<=m;i++) { int p1=bia[i].lef,p2=bia[i].righ; if ((p1!=p2)&& (!((p1==lastx)&&(p2==lasty)))) {add(p1,p2); ru[p2]++;} lastx=p1;lasty=p2; } for (int i=1;i<=num;i++) if (ru[i]==0) q.push(i),side[i]=size[i],dp[i]=1;
//dp tuopu(); for (int i=1;i<=num;i++) if (side[i]==ans) ans2=(ans2+dp[i])%mo;
//枚举哪些点是链的终点来统计答案 printf("%d\n%lld\n",ans,ans2); return 0; }