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

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1093


首先我们将原图求强连通然后缩点,显然我们在一个强连通里面所有的点都取,然后要在缩点后的图中取一条权值和最大的链,直接dp就行了。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <string.h>
#include <queue>
#include <vector>
#include <algorithm>
#include <cassert>
#include <set>
#include <map>
#include <cmath>
#include <ctime>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
#define clr(a,x) memset(a,(x),sizeof(a))
#define eps 1e-8
#define LL long long
#define mp make_pair
const int maxn=100000+5;
struct Node
{
    int v;
    Node * next;
}*first[maxn],edges[maxn*20];
int ptr;
vector<int> adj[maxn];
void add(int u,int v)
{
    edges[++ptr].v=v;
    edges[ptr].next=first[u];
    first[u]=&edges[ptr];
}
int n,m,mod;
 
void input()
{
    ptr=0; clr(first,0);
    while(m--) {
        int u,v; scanf("%d%d",&u,&v);
        add(u,v);
    }
}
 
 
int sccno[maxn],low[maxn],scc_cnt,dfn,pre[maxn];
vector<int> scc[maxn];
stack<int> S;
inline int min(int a,int b) { return a<b?a:b; }
inline int max(int a,int b) { return a>b?a:b; }
 
int dfs(int u)
{
    pre[u]=low[u]=++dfn;
    S.push(u);
    for(Node*p=first[u];p;p=p->next) {
        int v=p->v;
        if(!pre[v]) low[u]=min(low[u],dfs(v));
        else if(!sccno[v]) low[u]=min(low[u],pre[v]);
    }
    if(low[u]==pre[u]) {
        scc[++scc_cnt].clear();
        for(;;) {
            int x=S.top(); S.pop();
            sccno[x]=scc_cnt;
            scc[scc_cnt].push_back(x);
            if(x==u) break;
        }
    }
    return low[u];
}
 
void find_scc()
{
    clr(sccno,0); clr(pre,0); clr(low,0);
    dfn=scc_cnt=0;
    rep(i,1,n+1) if(!pre[i]) dfs(i);
}
 
int ind[maxn],q[maxn],front,rear;
int value[maxn];
bool vis[maxn];
void build()
{
    clr(vis,0); clr(ind,0);
    rep(i,1,scc_cnt+1) {
        vis[i]=true;
        S.push(i);
        adj[i].clear(); value[i]=scc[i].size();
        rep(j,0,scc[i].size()) {
            int u=scc[i][j];
            for(Node * p=first[u];p;p=p->next) {
                int v=p->v;
                if(!vis[sccno[v]]) { ++ind[sccno[v]]; adj[sccno[u]].push_back(sccno[v]); S.push(sccno[v]); }
                vis[sccno[v]]=true;
            }
        }
        while(S.size()) { vis[S.top()]=false; S.pop(); }
    }
}
 
LL sum[maxn],dp[maxn];
void solve()
{
    find_scc();
    build();
    front=rear=0;
    n=scc_cnt;
    rep(i,1,n+1)
    if(ind[i]==0) q[rear++]=i;
    while(front<rear) {
        int u=q[front++];
        rep(i,0,adj[u].size()) {
            int v=adj[u][i];
            if(--ind[v]==0) q[rear++]=v;
        }
    }
    clr(sum,0); clr(dp,0);
    rrep(i,rear-1,0) {
        int u=q[i]; dp[u]=value[u];
        if(adj[u].size()==0) {
            sum[u]=1;
            continue;
        }
        LL num=0,maxsize=0;
        rep(j,0,adj[u].size()) {
            int v=adj[u][j];
            if(maxsize<dp[v]) {
                maxsize=dp[v];
                num=sum[v];
            } else if(maxsize==dp[v]) num=(num+sum[v])%mod;
        }
        dp[u] += maxsize;
        sum[u] = num;
    }
    LL num=0,maxsize=0;
    rep(i,1,n+1)
    if(dp[i]>maxsize)
    {
        maxsize=dp[i];
        num=sum[i];
    } else if(dp[i]==maxsize) {
        num+=sum[i];
        num%=mod;
    }
    printf("%lld\n%lld\n",maxsize,num);
}
 
int main()
{
   // freopen("in.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&mod)==3) {
        input();
        solve();
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值