tyvj——P3524 最大半連通子圖
描述
輸入格式
第一行包含兩個整數N,M,X。N,M分別表示圖G的點數與邊數,X的意義如上文所述。接下來M行,每行兩個正整數a, b,表示一條有向邊(a, b)。圖中的每個點將編號為1,2,3…N,保證輸入中同一個(a,b)不會出現兩次。
輸出格式
應包含兩行,第一行包含一個整數K。第二行包含整數C Mod X.
測試樣例1
輸入
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
輸出
3
3
備註
對於20%的數據, N ≤18;
對於60%的數據, N ≤10000;
對於100%的數據, N ≤100000, M ≤1000000;
對於100%的數據, X ≤10^8。
最開始的時候沒有考慮到有環的時候,他可以連續跑,就沒有進行縮點,結果就只能A第二個點
後來wa掉以後發現如果有環的時候不進行縮點的話,由於兩個不相同的半聯通子圖滿足他們至少有一個點不相同,而如果按照我上面的思路的話我們下面的圖跑出來會是3個半連通子圖,而且最長的鏈會是3而正確結果是2 1
這樣的話我們就必須縮點了,我們先tarjan求強連通分量,然後在進行縮點,對跑出來的新圖進行拓撲排序,然後在拓撲排序裏面加dp。
仔細考慮了一下,好像我dfs然後在加暴力枚舉根本就不可行、、、 #include<queue> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define N 1100000 using namespace std; bool vis[N],vist[N]; int n,m,x,y,s,tot,tat,mod,ans1,ans2,top,tim; int20分tle代碼in[N],ss[N],dfn[N],low[N],head[N],head1[N],ans[N],sum[N],stack[N],belong[N]; int read() { int x=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } struct Edge { int to,from,next; }edge[N],edge1[N]; int add(int x,int y) { tot++; edge[tot].to=y; edge[tot].from=x; edge[tot].next=head[x]; head[x]=tot; } int add1(int x,int y) { tat++; edge1[tat].to=y; edge1[tat].from=x; edge1[tat].next=head1[x]; head1[x]=tat; } int tarjan(int now) { dfn[now]=low[now]=++tim; vis[now]=true; stack[++top]=now; for(int i=head[now];i;i=edge[i].next) { int t=edge[i].to; if(vis[t]) low[now]=min(low[now],dfn[t]); else if(!dfn[t]) tarjan(t),low[now]=min(low[now],low[t]); } if(low[now]==dfn[now]) { s++,belong[now]=s,ss[s]++; for(;stack[top]!=now;top--) belong[stack[top]]=s,vis[stack[top]]=false,ss[s]++; vis[now]=false,top--; } } int shink_point() { for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].next) if(belong[i]!=belong[edge[j].to]) add1(belong[i],belong[edge[j].to]); } int dfs(int x) { vist[x]=true; for(int i=head1[x];i;i=edge1[i].next) { int t=edge1[i].to; if(!vist[t]) dfs(t); in[t]=1; } vist[x]=false; } int tpsort(int s,int * in) { memset(sum,0,sizeof(sum)); queue<int>q; q.push(s);sum[s]=ss[s]; while(!q.empty()) { int x=q.front();q.pop(); for(int i=head1[x];i;i=edge1[i].next) { int t=edge1[i].to; if(in[t]==0) continue; in[t]--; if(in[t]==0) q.push(t); sum[t]=max(sum[t],sum[x]+ss[t]); } } } int main() { n=read(),m=read();mod=read(); for(int i=1;i<=m;i++) x=read(),y=read(),add(x,y); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); shink_point(); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { if(vis[belong[i]]) continue; vis[belong[i]]=true; memset(in,0,sizeof(in)); dfs(belong[i]);tpsort(belong[i],in); sort(sum+1,sum+1+n); ans[i]=sum[n]; ans1=max(ans1,ans[i]); } for(int i=1;i<=s;i++) if(ans[i]==ans1) ans2++; printf("%d\n%d\n",ans1,ans2); return 0; }
怎麽跑??
我們先考慮一個問題:在tarjan縮完點以後我們在建新圖的時候一定會建出重邊來,但是我們要進行拓撲排序的話就不可以有重邊,所以我們要在進行縮點後建圖的時候一定要判斷這條邊是否是重邊,我們用一個map數組來判斷。
然後我們在拓撲排序裏面跑dp,為什麽要用拓撲排序??因為通拓撲排序可以很容易的找出最長鏈。
怎麽dp?? 我們在第一部找出它的最大半聯通子圖的時候,其實找的就是最長鏈,我們把它最長鏈裏面的權值進行合並就行。我們用一個ans記錄到達當前點的最大權值,用v表示當前節點,用x表示與v連通那個點。由於我們有好幾條路徑可以到達v點,而我們要統計的是最大的半連通子圖的大小,所以我們在對當前點更新的時候則為ans[v]=max(ans[v],ans[x])為什麽是這樣??因為我們對於每一條鏈的ans[x]是一直在更新的。這樣我們就可以把最大的半聯通子圖統計出來。ans1=max(ans1,ans[i]).其次我們還要統計方案數。我們用數組dp記錄到當前點的方案數,用數組deep記錄到當前點的子圖的大小, 然後我們判斷這個點的deep值是否等於他父節點的deep值(暫且這樣叫吧、、)如果相等的話就說明出現了另一種方案數,那麽dp[t]=dp[t]+dp[x](加法原理其內容是:做一件事情,完成它有N類方式,第一類方式有M1種方法,第二類方式有M2種方法,……,第N類方式有M(N)種方法,那麽完成這件事情共有M1+M2+……+M(N)種方法。)如果當前點的deep小與其父節點的deep那麽我們對其dp進行修改,dp[t]=dp[x],deep[t]=deep[x]
#include<map> #include<queue> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define N 110000 using namespace std; bool vis[N],vist[N]; int n,m,x,y,s,tot,tat,mod,ans1,ans2,top,tim; int in[N],dp[N],dfn[N],low[N],deep[N],head[1100000],head1[1100000],ans[N],sum[N],stack[N],belong[N]; int read() { int x=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } struct Edge { int to,from,next; }edge[1100000],edge1[1100000]; int add(int x,int y) { tot++; edge[tot].to=y; edge[tot].from=x; edge[tot].next=head[x]; head[x]=tot; } int add1(int x,int y) { tat++; edge1[tat].to=y; edge1[tat].from=x; edge1[tat].next=head1[x]; head1[x]=tat; } int tarjan(int now) { dfn[now]=low[now]=++tim; vis[now]=true; stack[++top]=now; for(int i=head[now];i;i=edge[i].next) { int t=edge[i].to; if(vis[t]) low[now]=min(low[now],dfn[t]); else if(!dfn[t]) tarjan(t),low[now]=min(low[now],low[t]); } if(low[now]==dfn[now]) { s++,belong[now]=s,sum[s]++; for(;stack[top]!=now;top--) belong[stack[top]]=s,vis[stack[top]]=false,sum[s]++; vis[now]=false,top--; } } map<int,int>ma[N]; int shink_point() { for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].next) if(belong[i]!=belong[edge[j].to]) if((++ma[belong[i]][belong[edge[j].to]])==1) { add1(belong[i],belong[edge[j].to]); in[belong[edge[j].to]]++; } } int tpsort() { queue<int>q; for(int i=1;i<=s;i++) if(!in[i]) q.push(i),dp[i]=1; while(!q.empty()) { int x=q.front();q.pop();ans[x]+=sum[x],deep[x]+=sum[x]; for(int i=head1[x];i;i=edge1[i].next) { int t=edge1[i].to; in[t]--; if(!in[t]) q.push(t); ans[t]=max(ans[t],ans[x]); if(deep[t]==deep[x]) dp[t]=(dp[t]+dp[x])%mod; else if(deep[t]<deep[x]) dp[t]=dp[x],deep[t]=deep[x]; } } for(int i=1;i<=n;i++) ans1=max(ans1,ans[i]); for(int i=1;i<=n;i++) if(ans[i]==ans1) ans2=(ans2+dp[i])%mod; } int main() { n=read(),m=read();mod=read(); for(int i=1;i<=m;i++) x=read(),y=read(),add(x,y); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); shink_point(); tpsort(); printf("%d\n%d\n",ans1,ans2); return 0; }
tyvj——P3524 最大半連通子圖