首先比较明显的是如果存在一个半连通子图,我们将其中的环缩成点,那么该图仍为半连通子图,这样我们就可以先将整张图缩点,重新构图,新图为拓扑图,记录每个新的点表示的强连通分量中点的个数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\n",x,y);
}
 
void tarjan(int x){
    //printf("%d %d\n",x,fa);
    low[x]=dfn[x]=++time;
    stack[++tot]=flag[x]=x;
    //for (int i=1;i<=tot;i++) printf("%d ",stack[i]); printf("\n");
    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=-;
        while (cur!=x){
            cur=stack[tot--];
            flag[cur]=;
            col[cur]=size;
            num[size]++;
        }
        size++;
    }
}
 
void dfs(int x){
    int cur=;
    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\n",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]]=; father[other[p]]=x;
    }
    if (!ans[x]) ans[x]=;
    ans[x]%=d39;
    while (father[stack[tot]]==x) flag[stack[tot--]]=;
}
 
int main(){
    int x,y;
    scanf("%d%d%d",&n,&m,&d39); size=n+;
    for (int i=;i<=m;i++) scanf("%d%d",&x,&y),connect(x,y);
    for (int i=;i<=n;i++) if (!low[i]) tarjan(i);
    //for (int i=1;i<=n;i++) printf("%d %d %d\n",col[i],low[i],dfn[i]);
    for (int i=;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\n",i,num[i]);
    for (int i=n+;i<size;i++) if (!in[i]) dfs(i);
    //for (int i=n+1;i<size;i++) printf("|%d %d\n",i,len[i]);
    for (int i=n+;i<size;i++) p1=max(p1,len[i]);
    for (int i=n+;i<size;i++) if (len[i]==p1) work(i);
    for (int i=n+;i<size;i++) if (len[i]==p1) (p2+=ans[i])%=d39;
    //for (int i=n+1;i<size;i++) printf("|%d %d\n",i,ans[i]);
    printf("%d\n%d\n",p1,p2);
    return ;
}

bzoj 1093 缩点+DP的更多相关文章

  1. BZOJ 1093 [ZJOI2007] 最大半连通子图(强联通缩点+DP)

    题目大意 题目是图片形式的,就简要说下题意算了 一个有向图 G=(V, E) 称为半连通的(Semi-Connected),如果满足图中任意两点 u v,存在一条从 u 到 v 的路径或者从 v 到 ...

  2. bzoj 1093 [ZJOI2007]最大半连通子图——缩点+拓扑

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1093 缩点+拓扑,更新长度的时候维护方案数. 结果没想到处理缩点后的重边,这样的话方案数会算 ...

  3. BZOJ 1093 [ZJOI2007]最大半连通子图

    1093: [ZJOI2007]最大半连通子图 Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 1986  Solved: 802[Submit][St ...

  4. POJ3160 Father Christmas flymouse[强连通分量 缩点 DP]

    Father Christmas flymouse Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 3241   Accep ...

  5. [bzoj 1093][ZJOI2007]最大半联通子图(强联通缩点+DP)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1093 分析: 首先肯定是先把强联通全部缩成一个点,然后成了一个DAG 下面要知道一点: ...

  6. BZOJ 1093 强连通缩点+DAG拓扑DP

    缩点后在一个DAG上求最长点权链 和方案数 注意转移条件和转移状态 if (nowmaxn[x] > nowmaxn[v]) { ans[v] = ans[x]; nowmaxn[v] = no ...

  7. BZOJ 1093: [ZJOI2007]最大半连通子图( tarjan + dp )

    WA了好多次... 先tarjan缩点, 然后题意就是求DAG上的一条最长链. dp(u) = max{dp(v)} + totu, edge(u,v)存在. totu是scc(u)的结点数. 其实就 ...

  8. bzoj 1093 [ ZJOI 2007 ] 最大半连通子图 —— 拓扑+DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1093 先缩点,然后就是找最长链,DP一下即可: 注意缩点后的重边!会导致重复计算答案. 代码 ...

  9. bzoj 1093 [ZJOI2007]最大半连通子图(scc+DP)

    1093: [ZJOI2007]最大半连通子图 Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 2286  Solved: 897[Submit][St ...

随机推荐

  1. 按Backspace键删除时,会出现^H

    按Backspace键删除时,会出现^H 2014-08-12 19:38 1180人阅读 评论(0) 举报 版权声明:本文为博主原创文章,未经博主允许不得转载. 在linux/unix 平台的经常使 ...

  2. loadrunner如何监控windows系统的资源

    1.测试客户端与服务器之间的网络,保证通信畅通 2.开启服务器端Windows中的如下两个服务,可见系统服务中查找,cmd输入:services.msc 如下图: Remote Registry需改为 ...

  3. cmake & make

    大家都知道,写程序大体步骤为: 1.用编辑器编写源代码,如.c文件. 2.用编译器编译代码生成目标文件,如.o. 3.用链接器连接目标代码生成可执行文件,如.exe. 但如果源文件太多,一个一个编译时 ...

  4. Visual C++中对运行时库的支持

    原文地址:http://blog.csdn.net/wqvbjhc/article/details/6612099 一.什么是C运行时库 1)C运行时库就是 C run-time library,是 ...

  5. ADC关键性能指标及误区

    ADC关键性能指标及误区 由于ADC产品相对于网络产品和服务器需求小很多,用户和集成商在选择产品时对关键指标的理解难免有一些误区,加之部分主流厂商刻意引导,招标规范往往有不少非关键指标作被作为必须符合 ...

  6. NetScaler + Wireshark = A Perfect Combination!

    NetScaler + Wireshark = A Perfect Combination! https://www.citrix.com/blogs/2014/05/03/netscaler-wir ...

  7. [清华集训2017]无限之环(infinityloop)

    description 题面 solution 一开始的思路是插头\(DP\),然而复杂度太高 考虑将网格图黑白染色后跑费用流 流量为接口数,费用为操作次数 把一个方格拆成五个点,如何连边请自行脑补 ...

  8. 运动员最佳匹配问题 KM算法:带权二分图匹配

    题面: 羽毛球队有男女运动员各n人.给定2 个n×n矩阵P和Q.P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞赛优势:Q[i][j]是女运动员i和男运动员j配合的女运动员竞赛优势. ...

  9. POJ3415:Common Substrings——题解

    http://poj.org/problem?id=3415 给定两个字符串A 和B,求长度不小于k 的公共子串的个数(可以相同). 论文题,和上道题(POJ2774)类似,首先想到现将AB串合并,然 ...

  10. POJ3041:Asteroids——题解

    http://poj.org/problem?id=3041 题目大意:激光可以干掉一整行或一整列陨石,求最少激光次数. —————————————————— 二分图匹配,对于每一个陨石将它的横纵坐标 ...