【tarjan】P2272 [ZJOI2007]最大半连通子图

17 篇文章 0 订阅

首先可以用tarjan进行缩点,因为缩点后的结果对答案不会造成影响

然后就可以按照拓扑序进行求解了

方案数可以用dp的方式去求

 

注意:要在缩完点之后进行去重的操作,否则方案数会被算大

 

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
int n,m,times,tot,head[maxn],dfn[maxn],low[maxn];
long long mod,in[maxn];
struct edge
{
	int to,nxt;
}e[maxm<<1];
void add(int x,int y)
{
	e[++tot].nxt=head[x];
	e[tot].to=y;
	head[x]=tot;
}
int col_num,top,inin[maxn],col[maxn],a[maxm],b[maxm],sta[maxn],sz[maxn];
/* 
void tarjan(int x)
{
	low[x]=dfn[x]=++times;
	sta[++top]=x;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int to=e[i].to;
		if(!dfn[to])
		{
			tarjan(to);
			low[x]=min(low[x],low[to]);
		}
		else if(!col[to])
		{
			low[x]=min(low[x],dfn[to]);
		}
	}	
	if(dfn[x]==low[x])
	{
		col[x]=++col_num;
		++sz[col_num];
		while(sta[top]!=x)
		{
			++sz[col_num];
			col[sta[top]]=col_num;
			top--;
		}
		top--;
	}
}*/
void tarjan(int x){
    dfn[x] = low[x] = ++ times;
    sta[ ++ top] = x;
    inin[x] = true;

    for(int i = head[x];i;i = e[i].nxt){
        int y = e[i].to;
        if(!dfn[y]){
            tarjan(y);
            low[x] = min(low[x],low[y]);
        }
        else if(inin[y]){
            low[x] = min(low[x], dfn[y]);
        }
    }

    if(dfn[x] == low[x]){
        int y;
        ++col_num;
        do{
            y = sta[top -- ];
            inin[y] = false;
            col[y] = col_num;
            sz[col_num] ++ ;
        }while(x != y);
    }
} 
int cnt[maxm];
bool cmp(int x,int y)
{
	if(a[x]!=a[y]) return a[x]<b[y];
	return b[x]<b[y];
}
void build()
{
	memset(head,0,sizeof(head)); tot=0;
	map <long long,int> st;
	for(int i=1;i<=m;i++)
	{
		a[i]=col[a[i]]; b[i]=col[b[i]];
		long long has=a[i]*100000ll+b[i];
		if((a[i]!=b[i]) && !st[has])
		{
			++inin[b[i]];
			add(a[i],b[i]);
			st[has]=1;
		}
	}
}
long long ans,dis[maxn];
void toposort()
{
	memset(in,0,sizeof(in));
    for(int i = col_num;i >= 1;-- i){
        if(!dis[i]){
            dis[i] = sz[i];
            in[i] = 1;
        }
        for(int j = head[i];j;j = e[j].nxt){
            int k = e[j].to;
            if(dis[k] < dis[i] + sz[k]){
                dis[k] = dis[i] + sz[k];
                in[k] = in[i];
            }
            else if(dis[k] == dis[i] + sz[k]){
                in[k] = (in[k] + in[i]) % mod;
            }
        }
    }
    long long maxx = 0,sum = 0;
    for(int i = 1;i <= n;++i){
        if(dis[i] > maxx){
            maxx = dis[i];
            sum = in[i];
        }
        else if(dis[i] == maxx){
            sum = (sum + in[i] ) % mod;
        }
    }
    printf("%lld\n%lld\n",maxx, sum);
}
long long anss;
void calc()
{
	for(int i=1;i<=n;i++)
	{
		if(dis[i]==dis[ans])
			anss=(anss+in[i])%mod;
	}
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d%lld",&n,&m,&mod);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i],&b[i]);
		add(a[i],b[i]);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			tarjan(i);
	build();
	toposort();
//	calc();
//	printf("%lld\n%lld",dis[ans],anss);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值