ZOJ Problem Set - 3795(缩点拓补)

题意:每条信息说明了两个一定不在一个集合里的人,求最少情况集合可以划分为多少子集。

一看就是拓补树的最高层数,但题意中隐含了可能有环(>=关系偏序),所以要先缩点,再拓补。当然,缩点之后图中没有环,直接dfs记忆化也是ok的。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define rev(i,a,b) for(int i=(a);i>=(b);i--)
#define clr(a,x) memset(a,x,sizeof a)
#define INF 0x3f3f3f3f
typedef long long LL;
using namespace std;

const int maxn=100005;
const int maxm=300005;

int first[maxn],ecnt,u[maxm],v[maxm],nex[maxm];
int low[maxn],dfn[maxn],stck[maxn],belong[maxn];
int indx,top,scc;
bool ins[maxn];
int num[maxn];
int in[maxn],out[maxn];
int h[maxn];
int n,m;
int ecntt,firstt[maxn],uu[maxm],vv[maxm],nexx[maxm];

void tarjan(int u)
{
    low[u]=dfn[u]=++indx;
    stck[top++]=u;
    ins[u]=1;
    for(int e=first[u];~e;e=nex[e])
    {
        if(!dfn[v[e]])
        {
            tarjan(v[e]);
            low[u]=min(low[u],low[v[e]]);
        }
        else if(ins[v[e]])low[u]=min(low[u],dfn[v[e]]);
    }
    if(low[u]==dfn[u])
    {
        int v;
        scc++;
        do
        {
            v=stck[--top];
            ins[v]=false;
            belong[v]=scc;
            num[scc]++;
        }while(v!=u);
    }
}
void solve(int n)
{
    clr(dfn,0);
    clr(ins,0);
    clr(num,0);
    indx=scc=top=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);
}
void add_(int a,int b)
{
    u[ecnt]=a;
    v[ecnt]=b;
    nex[ecnt]=first[a];
    first[a]=ecnt++;
}
void add__(int a,int b)
{
    uu[ecntt]=a;
    vv[ecntt]=b;
    nexx[ecntt]=firstt[a];
    firstt[a]=ecntt++;
}
void init()
{
    ecnt=0;
    clr(first,-1);
}
bool topsort(int n,int first[],int v[],int nex[])
{
    queue<int>q;
    memset(h,0,sizeof h);
    for(int i=1;i<=n;i++)
        if(!in[i])q.push(i),h[i]=num[i];
    int cur=0;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        cur++;
        for(int e=first[x];~e;e=nex[e])
        if(v[e]!=-1){
            h[v[e]]=max(h[v[e]],h[x]+num[v[e]]);
            if(--in[v[e]]==0)q.push(v[e]);
        }
    }
    return cur==n;
}
int dfs(int root)
{
    if(h[root])return h[root];
    h[root]=num[root];
    for(int e=firstt[root];~e;e=nexx[e])
    {
        dfs(vv[e]);
        h[root]=max(h[root],h[vv[e]]+num[root]);
    }
    return h[root];
}
int gettop(int n)
{
    clr(h,0);
    int ans=1;
    for(int i=1;i<=n;i++)
    if(!in[i])
    {
        ans=max(ans,dfs(i));
    }
    return ans;
}
void suodian()
{
    ecntt=0;clr(firstt,-1);
    clr(in,0);
    for(int e=0;e<ecnt;e++)
    {
        int a=belong[u[e]];
        int b=belong[v[e]];
        if(a!=b)add__(a,b),in[b]++;
    }
}
int main()
{
    int a,b;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            add_(a,b);
        }
        solve(n);
        suodian();
        topsort(scc,firstt,vv,nexx);
        int ans=1;
        for(int i=1;i<=scc;i++)
            ans=max(ans,h[i]);
        /*int ans=gettop(scc);*/
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-10 14:15:29

ZOJ Problem Set - 3795(缩点拓补)的相关文章

ZOJ Problem Set - 3820 Building Fire Stations 【树的直径 + 操作 】

题目:problemId=5374" target="_blank">ZOJ Problem Set - 3820 Building Fire Stations 题意:给出n个点,n-1条边的一棵树.然后要在两个点上建立两个消防站.让全部点的到消防站最大距离的点的这个距离最小. 分析:首先先求这个树的直径.然后在树的直径的中点处把树分成两棵树.然后在把两棵树分别取中点的最大值就是ans值. 这个题目数据有点水了感觉... AC代码: #include <cstdi

ZOJ Problem Set - 1025解题报告

ZOJ Problem Set - 1025 题目分类:动态规划 原题地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1025   题目大意就是有很多木头,都有各自的长度和重量.现在要加工这些木头,如果加工某根木头的长度和重量大于等于它上一根木头的长度和重量,那么加工它不需要时 间,否则要花1分钟.现给出一堆木头的长度和重量,要求加工完这堆木头可以花的最少时间.例如给出5根木头长度重量分别为(4,9), (5,2),

ZOJ Problem Set - 3321 并查集

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3321 Circle Time Limit: 1 Second      Memory Limit: 32768 KB Your task is so easy. I will give you an undirected graph, and you just need to tell me whether the graph is just a circle.

【BZOJ3036】绿豆蛙的归宿 拓补排序+概率

[BZOJ3036]绿豆蛙的归宿 Description 随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿. 给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度.绿豆蛙从起点出发,走向终点.到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K .现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少? Input 第一行: 两个整数 N M,代表图中有N个点.M条边第二行到第 1+M 行: 每

ZOJ Problem Set - 3804 YY&#39;s Minions

学习:换一个角度考虑问题.YY's Minions Time Limit: 2 Seconds      Memory Limit: 65536 KB Despite YY's so much homework, she would like to take some time to play with her minions first. YY lines her minions up to an N*M matrix. Every minion has two statuses: awake

【BZOJ2815】[ZJOI2012]灾难 拓补排序+LCA

[BZOJ2815][ZJOI2012]灾难 题目描述 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难. 学过生物的阿米巴告诉小强,草原是一个极其稳定的生态系统.如果蚂蚱灭绝了,小鸟照样可以吃别的虫子,所以一个物种的灭绝并不一定会引发重大的灾难. 我们现在从专业一点的角度来看这个问题.我们用一种叫做食物网的有向图来描述生物之间的关系: 一个食物网有N个点,代表N种生物,如果生物

ZOJ Problem Set - 3195 Design the city 【Tarjan离线LCA】

题目:ZOJ Problem Set - 3195 Design the city 题意:给出一个图,求三点的连起来的距离. 分析:分别求出三点中任意两点的距离 / 2  = ans AC代码: #include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; #define N 50010 #define M 20010 struc

ZOJ Problem Set - 3229 Shoot the Bullet 【有上下界网络流+流量输出】

题目:ZOJ Problem Set - 3229 Shoot the Bullet 分类:有源有汇有上下界网络流 题意:有 n 天和 m 个girls,然后每天给一部分girls拍照,每个girls 有拍照的下限,即最少要拍这么多张,然后每天有k个女孩拍照,摄影师最多可以拍num张,然后 k 个女该每天拍照数量值有上下限,然后问你有没有满足这样条件的给女孩拍照的最大方案,然后按照输入输出每天给女孩拍照的张数. 做这道题目推荐先做:这儿 分析:首先它让你判断能不能满足条件. 按照题目给出的条件很

ZOJ Problem Set - 3203 Light Bulb 【三分法】

题目:ZOJ Problem Set - 3203 Light Bulb 题意: 如图,有个人在地上走,然后他的影子可以投影到墙上或者地上,问影子最长是多少? 分析: 我们知道,三分法是解决一个凹或凸函数的极大极小值,发现这个影子从刚好投影到右下角开始便是一个凸函数,他的影子长度是先递增后递减的,所以可以用三分法. 三分法的原理: AC代码: #include <cstdio> #include <cstring> #include <vector> #include