其他分享
首页 > 其他分享> > [POI2012]FES-Festival

[POI2012]FES-Festival

作者:互联网

P3530[POI2012]FES-Festival

@

目录

题目

有一个数列 $a$ 。现给定多组限制,限制分成 2 类,第一类是 $ax+1=ay$ ,有 $m1$ 个; 第二类是 $ax≤ay$ ,有 $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;
}

标签:限制,POI2012,610,上限,SCC,Festival,int,FES
来源: https://www.cnblogs.com/gdfzlcx/p/16583471.html