BZOJ 1512 [POI2006]Pro-Professor Szu Tarjan縮點+拓撲DP

題意:
n個別墅以及一個主建築樓,從每個別墅都有很多種不同方式走到主建築樓,其中不同的定義是(每條邊可以走多次,如果走邊的順序有一條不同即稱兩方式不同)。
詢問最多的不同方式是多少,以及有多少個別墅有這麼多方式,按照順序輸出別墅編號。
如果最多不同方式超過了36500那麼都視作zawsze
解析:
容易想到把邊反向,問題轉化成求從主建築樓走向各個點的方案數。
對於一個強連通分量,顯然我們可以看做是一個點,所以首先把圖縮點。
縮點之後
我們設f[i] 表示走到第i個點的方案數。
顯然f[i]=f[j](newedge(j,i))
並且特別注意的是,初始時,f[belong[主建築]]=1。
其次任意一個對於任意一個siz[i]>1的點來說,如果f[i]>0的話,那麼顯然有無數種走法,直接設爲maxn+1。
然後我們觀察這其實就是個拓撲圖上做DP的過程。
直接拓撲圖上DP。
最後掃一遍統計解即可。
複雜度:O(常數*n);
代碼:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000100
#define maxn 36500
using namespace std;
int n,m,rev_cnt;
int rev_head[N];
struct Line
{
    int x,y;
}l[N];
struct node
{
    int from,to,next;
}rev_edge[N];
void init()
{
    memset(rev_head,-1,sizeof(rev_head));
    rev_cnt=1;
}
void edgeadd(int from,int to,node *edge,int *head,int &cnt)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].next=head[from];
    head[from]=cnt++;
}
int deep[N],low[N],sta[N],ins[N],belong[N],top,cnt_block,tot;
void tarjan(int now)
{
    deep[now]=low[now]=++tot;
    sta[++top]=now,ins[now]=1;
    for(int i=rev_head[now];i!=-1;i=rev_edge[i].next)
    {
        int to=rev_edge[i].to;
        if(!deep[to])
        {
            tarjan(to);
            low[now]=min(low[now],low[to]);
        }else if(ins[to])low[now]=min(low[now],deep[to]);
    }
    if(deep[now]==low[now])
    {
        cnt_block++;
        int t=-1;
        do
        {
            t=sta[top--];
            ins[t]=0;
            belong[t]=cnt_block;
        }while(t!=now);
    }
}
bool tag[N];
int in[N];
void re_build()
{
    for(int i=1;i<=m;i++)
    {
        int x=l[i].x,y=l[i].y;
        if(belong[x]==belong[y]){tag[belong[x]]=1;continue;}
        edgeadd(belong[x],belong[y],rev_edge,rev_head,rev_cnt);
        in[belong[y]]++;
    }
}
int f[N];
void Topsort()
{
    queue<int>q;
    for(int i=1;i<=cnt_block;i++)
        if(!in[i])q.push(i);
    f[belong[n]]=1;
    if(tag[belong[n]])f[belong[n]]+=maxn;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=rev_head[u];i!=-1;i=rev_edge[i].next)
        {
            int to=rev_edge[i].to;
            f[to]+=f[u];
            if(f[to]>maxn)f[to]=maxn+1;
            in[to]--;
            if(!in[to])
            {
                if(tag[to]&&f[to]>0)f[to]=maxn+1;
                q.push(to);
            }
        }
    }
}
void calc()
{
    int ans=-1;top=0;
    for(int i=1;i<n;i++)
        if(f[belong[i]]>ans)
            ans=f[belong[i]];
    for(int i=1;i<n;i++)
        if(f[belong[i]]==ans)
            sta[++top]=i;
    printf(ans==maxn+1?"zawsze\n":"%d\n",ans);
    printf("%d\n",top);
    for(int i=1;i<=top;i++)
        printf("%d ",sta[i]);
    puts("");
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("1512.in","r",stdin);
        freopen("1512.out","w",stdout);
    #endif
        init();
        scanf("%d%d",&n,&m);n++;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            swap(x,y);
            edgeadd(x,y,rev_edge,rev_head,rev_cnt);
            l[i].x=x,l[i].y=y;
        }
        for(int i=1;i<=n;i++)
            if(!deep[i])tarjan(i);
        init();
        re_build();
        Topsort();
        calc();
    #ifndef ONLINE_JUDGE
        fclose(stdin);fclose(stdout);
    #endif
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章