[POI2012]FES-Festival

博客分析了POI2012中的一道题目FES-Festival,涉及数列限制条件。通过建立差分约束系统,探讨如何将问题转化为寻找最大不同取值的数量。文章指出,在强连通图中,通过确定值域的两端并利用限制条件,可以确定上限,并进一步解释如何通过强连通分解和DAG(有向无环图)来求解问题。最后,提供了代码实现。
摘要由CSDN通过智能技术生成

P3530[POI2012]FES-Festival

题目

有一个数列 a a a 。现给定多组限制,限制分成 2 类,第一类是 a x + 1 = a y ax+1=ay ax+1=ay ,有 m 1 m1 m1 个; 第二类是 a x ≤ a y ax≤ay axay ,有 m 2 m2 m2 个。求这些数最多有多少种不同的取值。
详情请查看题目

分析

首先建出差分约束系统。然后它要你求的是不同成绩的最大数量,而差分约束只擅长做(两个变量的差的最大 / 小值)这种问题,如何转化过去呢?
注意到对于任意差分约束系统,这个点与点的连通性是非常混乱的,无法判断点和点和点和点之间到底能不能拉开,还是必须重叠的什么情况。我们考虑一个强连通图,这里面每个点到每个点都可以互相到达,也就是说任意两个变量的差都有有限的上下限。那就很好了呀,我们至少知道了它的不同成绩的数量的一个上限:我们总要选两个点当作值域的两端对不对,而每两个点的差都是有有限上限的,于是取那个最大的距离(也就是最大的一对点之间的最短路)就是上限了。接下来考虑上限能不能达到,很容易证明可以,因为在那条最大的最短路上,一路走的过程中每次加一,都一定是第一种限制做的功劳,而第一种限制是定量限制,限制死了的,所以一旦把值域的两端确定下来,中间都填的满满的。

至此我们已经知道一个强连通图的求法了。考虑将原图强连通分解,然后每个 SCC 求出来答案,再缩点发现是个 DAG。DAG 也是个连通性特殊的有向图,它也好办了呀,没有任意两个 SCC 间有相互的限制,也就是说我们可以沿着边的方向将两个 SCC 之间的距离无限拉大,这样每个 SCC 就是独立的了,把所有 SCC 答案加起来也就达到了上限。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m1,m2,d[610][610];
struct edge
{
	int x,w;
};
vector<edge> v[610];
int sum,st[610],top,ans,res;
int dfn[610],low[610],ins[610],col[610],cnt[610];
void Tarjan(int x)
{
	low[x]=dfn[x]=++res;
	st[++top]=x;
	ins[x]=1;
	int y;
	for(int i=0;i<v[x].size();i++)
	{
		y=v[x][i].x;
		if(!dfn[y])
		{
			Tarjan(y);
			low[x]=min(low[y],low[x]);
		}
		else if(ins[y])low[x]=min(dfn[y],low[x]);
	}
	if(dfn[x]==low[x])
	{
       	sum++;
     	do
		{
     	    y=st[top--];
     	    col[y]=sum;
          	ins[y]=0;
      	}while(y!=x);
    }
}
int a[610];
int main()
{
	scanf("%d%d%d",&n,&m1,&m2);
	memset(d,0x3f3f,sizeof(d));
	for(int i=1;i<=n;i++)d[i][i]=0;
	for(int i=1,x,y;i<=m1;i++)
	{
		scanf("%d%d",&x,&y);
		v[x].push_back(edge{y,1});
		v[y].push_back(edge{x,-1});
		d[x][y]=min(d[x][y],1);
		d[y][x]=min(d[y][x],-1);
	}
	for(int i=1,x,y;i<=m2;i++)
	{
		scanf("%d%d",&x,&y);
		v[y].push_back(edge{x,0});
		d[y][x]=min(d[y][x],0);
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])Tarjan(i); 
	}
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			if(col[i]!=col[k]||d[i][k]==d[0][0])continue;
			for(int j=1;j<=n;j++)
			{
				if(col[i]!=col[j]||d[k][j]==d[0][0])continue;
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(d[i][i])
		{
			printf("NIE");
			return 0;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(col[i]==col[j])a[col[i]]=max(a[col[i]],d[i][j]);
		}
	}
	for(int i=1;i<=sum;i++)ans+=a[i]+1;
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值