[bzoj2788][Poi2012]Festival floyd 差分约束系统 tarjan

该博客介绍了Poi2012年比赛中的Festival问题,涉及到寻找n个正整数X,在满足m1+m2组特定约束条件(Xa+1=Xb或Xd<=Xc)的情况下,求集合{Xi}的最大可能大小。题目提供输入输出格式,并给出了样例解答。解题关键在于差分约束系统的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2788: [Poi2012]Festival

Time Limit: 30 Sec  Memory Limit: 64 MB
[ Submit][ Status][ Discuss]

Description


有n个正整数X1,X2,...,Xn,再给出m1+m2个限制条件,限制分为两类:

1. 给出a,b (1<=a,b<=n),要求满足Xa + 1 = Xb

2. 给出c,d (1<=c,d<=n),要求满足Xc <= Xd

在满足所有限制的条件下,求集合{Xi}大小的最大值。



Input


第一行三个正整数n, m1, m2 (2<=n<=600, 1<=m1+m2<=100,000)。

接下来m1行每行两个正整数a,b (1<=a,b<=n),表示第一类限制。

接下来m2行每行两个正整数c,d (1<=c,d<=n),表示第二类限制。



Output

一个正整数,表示集合{Xi}大小的最大值。


如果无解输出NIE。

Sample Input

4 2 2

1 2

3 4

1 4

3 1

Sample Output

3

HINT

|X3=1, X1=X4=2, X2=3


这样答案为3。容易发现没有更大的方案。



Source

差分约束建图先
首先跑一遍Floyd,如果一个点到自己的距离都小于0了,NIE!
然后tarjan缩点,可以想到各个环中各不影响(其实是贪到)
在一个环中如何统计最大点数?
我们可以知道,由于距离绝对值不大于1,假设在数轴上,一个环内所有点紧靠,那么可以取的个数就是 环内最大距离+1
这道题我跑了16000+,有人跑了400+,都怪floyd太暴力了,由于我是懒癌所以。。就floyd吧
非常暴力的代码,邻接表还有个没用的v,懒得去了
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 600 + 5;
const int M = 2000000 + 5;
struct Edge{
	int to,next;
}e[M];
int last[N*2],cnt;
void insert( int u, int v, int w ){ e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt; }
int mp[N][N],dfn[N],low[N],scc,sta[N],cot,n,m1,m2,top,bel[N],ans; bool inq[N];
void tarjan( int x ){
	low[x] = dfn[x] = ++cot;
	sta[++top] = x; inq[x] = 1;
	for( int i = last[x]; i; i = e[i].next )
		if( !dfn[e[i].to] ) tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]);
		else if( inq[e[i].to] ) low[x] = min(low[x],dfn[e[i].to]);
	if( dfn[x] == low[x] ){
		int now = 0; scc++;
		while( now != x ){
			now = sta[top--];
			bel[now] = scc;
			inq[now] = 0;
		}
	}
}
int main(){
	scanf("%d%d%d", &n, &m1, &m2);
	memset(mp,0x3f,sizeof(mp));
	for( int i = 1,u,v; i <= m1; i++ ){
		scanf("%d%d", &u, &v);
		mp[u][v] = min(mp[u][v],1); mp[v][u] = min(mp[v][u],-1);
		insert( u, v, 1 ); insert( v, u, -1 );
	}
	for( int i = 1,u,v; i <= m2; i++ ){
		scanf("%d%d", &u, &v);
		mp[v][u] = min(mp[v][u],0);
		insert( v, u, 0 );
	}
	for( int i = 1; i <= n; i++ ) mp[i][i] = 0;
	for( int k = 1; k <= n; k++ )
		for( int i = 1; i <= n; i++ )
			for( int j = 1; j <= n; j++ )
				mp[i][j] = min( mp[i][j], mp[i][k]+mp[k][j] );
	for( int i = 1; i <= n; i++ ) if( mp[i][i] < 0 ){ puts("NIE"); return 0; }
	for( int i = 1; i <= n; i++ ) if( !dfn[i] ) tarjan(i);
	for( int i = 1; i <= scc; i++ ){
		int now = 0;
		for( int j = 1; j <= n; j++ )
			if( bel[j] == i )
				for( int k = 1; k <= n; k++ )
					if( bel[k] == i )
					 now = max(now,mp[j][k]);
		ans += now+1;
	}
	printf("%d\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值