• bzoj 1093 缩点+DP


      首先比较明显的是如果存在一个半连通子图,我们将其中的环缩成点,那么该图仍为半连通子图,这样我们就可以先将整张图缩点,重新构图,新图为拓扑图,记录每个新的点表示的强连通分量中点的个数num[i],那么我们就可以DP了,新图中的每一条链都为原图的半连通子图,这样我们找到新图中的最长链就行了,找入度为0的点dfs做树上DP,这样我们可以知道每个点的len[i]代表从这个点开始的最长链的长度,len[i]=max(len[son of i])+num[i],然后我们求出来了第一问,对于第二问,我们需要找len[i]=ans1的点做dfs,然后设ans[i]为以i为根的子树的方案数,那么ans[i]+=ans[son of i] (len[i]=len[son of i]+num[i]),因为我们需要找最长链上的点来更新答案,这样最后再累计答案就好了。

      反思:开始题中说没有重边,但是没有考虑到重构图之后的图是可能有重边的,这样第二问的答案就可能会被重复累加,所以我们DP的时候可以维护一个栈,和每个栈中元素的父亲,这样对于一个点枚举子节点,如果子节点没有在栈中出现过,那么就累加答案,该子节点进栈,dfs最后的时候再弹出所有栈中x的子节点。

     

    /**************************************************************
        Problem: 1093
        User: BLADEVIL
        Language: C++
        Result: Accepted
        Time:1992 ms
        Memory:45028 kb
    ****************************************************************/
     
    //By BLADEVIL
    #include <cstdio>
    #include <algorithm>
    #define maxn 200020
    #define maxm 4000040
     
    using namespace std;
     
    int n,m,d39,l,tot,time,size;
    int last[maxn],other[maxm],pre[maxm],stack[maxn],low[maxn],dfn[maxn],flag[maxn],col[maxn],num[maxn],in[maxn];
    int len[maxn],ans[maxn],father[maxn];
    int p1,p2;
     
    void connect(int x,int y){
        pre[++l]=last[x];
        last[x]=l;
        other[l]=y;
        //if (x>n||y>n) printf("%d %d
    ",x,y);
    }
     
    void tarjan(int x){
        //printf("%d %d
    ",x,fa);
        low[x]=dfn[x]=++time;
        stack[++tot]=flag[x]=x;
        //for (int i=1;i<=tot;i++) printf("%d ",stack[i]); printf("
    ");
        for (int p=last[x];p;p=pre[p]){
            if (!dfn[other[p]]) tarjan(other[p]),low[x]=min(low[x],low[other[p]]); else
            if (flag[other[p]]) low[x]=min(low[x],dfn[other[p]]);
        }
        if (low[x]==dfn[x]){
            int cur=-1;
            while (cur!=x){
                cur=stack[tot--];
                flag[cur]=0;
                col[cur]=size;
                num[size]++;
            }
            size++;
        }
    }
     
    void dfs(int x){
        int cur=0;
        for (int p=last[x];p;p=pre[p]){
            if (!len[other[p]]) dfs(other[p]);
            cur=max(cur,len[other[p]]);
        }
        len[x]=cur+num[x];
        //printf(" %d %d
    ",x,len[x]);
    }
     
    void work(int x){
        for (int p=last[x];p;p=pre[p])
            if (len[other[p]]+num[x]==len[x]) {
                if (flag[other[p]]) continue;
                if (!ans[other[p]]) work(other[p]);
                ans[x]+=ans[other[p]];
                stack[++tot]=other[p]; flag[other[p]]=1; father[other[p]]=x;
        }
        if (!ans[x]) ans[x]=1;
        ans[x]%=d39;
        while (father[stack[tot]]==x) flag[stack[tot--]]=0;
    }
     
    int main(){
        int x,y;
        scanf("%d%d%d",&n,&m,&d39); size=n+1;
        for (int i=1;i<=m;i++) scanf("%d%d",&x,&y),connect(x,y);
        for (int i=1;i<=n;i++) if (!low[i]) tarjan(i);
        //for (int i=1;i<=n;i++) printf("%d %d %d
    ",col[i],low[i],dfn[i]);
        for (int i=1;i<=n;i++)
            for (int p=last[i];p;p=pre[p])
                if (col[i]!=col[other[p]]) connect(col[i],col[other[p]]),in[col[other[p]]]++;
        //for (int i=n+1;i<size;i++) printf("|%d %d
    ",i,num[i]);
        for (int i=n+1;i<size;i++) if (!in[i]) dfs(i);
        //for (int i=n+1;i<size;i++) printf("|%d %d
    ",i,len[i]);
        for (int i=n+1;i<size;i++) p1=max(p1,len[i]);
        for (int i=n+1;i<size;i++) if (len[i]==p1) work(i);
        for (int i=n+1;i<size;i++) if (len[i]==p1) (p2+=ans[i])%=d39;
        //for (int i=n+1;i<size;i++) printf("|%d %d
    ",i,ans[i]);
        printf("%d
    %d
    ",p1,p2);
        return 0;
    }
  • 相关阅读:
    Bzoj 2820: YY的GCD(莫比乌斯反演+除法分块)
    Cogs 2221. [SDOI2016 Round1] 数字配对(二分图)
    Cogs 750. 栅格网络(对偶图)
    最小环问题
    浅谈卡特兰数
    洛谷 P1744 采购特价商品
    HDU 1212 Big Number
    HDU 2108 Shape of HDU
    HDU 1029 Ignatius and the Princess IV
    HDU 1021 Fibonacci Again
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3574184.html
Copyright © 2020-2023  润新知