1. 程式人生 > >tyvj——P3524 最大半連通子圖

tyvj——P3524 最大半連通子圖

read str include 它的 必須 border 暴力 emp lose

P3524 最大半連通子圖 時間: 3000ms / 空間: 165536KiB / Java類名: Main

描述

技術分享

輸入格式

第一行包含兩個整數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;
int
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; }
20分tle代碼

怎麽跑??

我們先考慮一個問題:在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 最大半連通子圖