luogu P3530 [POI2012]FES-Festival

背景:

继续肝…
肝完这题差分约束也就告一段落了。

题目传送门:

https://www.luogu.org/problemnew/show/P3530

题意:

垃圾翻译。
有两种约束条件(每种有多个),对于每一个约束条件给出两个位置 x , y x,y x,y,使得 x x x位置和 y y y位置上的值满足所对应的约束条件。现在你需要构造一个满足所有约束条件的序列(值域为 [ 1 , n ] [1,n] [1,n]),使序列中不同元素的个数的尽可能大,求这个最大值。

思路:

显然是差分约束。
对于题目给的 x + 1 = y x+1=y x+1=y,我们可以得到不等式: x + 1 ≤ y ≤ x + 1 x+1≤y≤x+1 x+1yx+1
拆成两个式子 x + 1 ≤ y         ,         y ≤ x + 1 x+1≤y\ \ \ \ \ \ \ ,\ \ \ \ \ \ \ y≤x+1 x+1y       ,       yx+1
分别来看: x + 1 ≤ y x+1≤y x+1y可以得到 x &lt; = y − 1 x&lt;=y-1 x<=y1,即为 i n s ( y , x , − 1 ) ins(y,x,-1) ins(y,x,1) y ≤ x + 1 y≤x+1 yx+1,即为 i n s ( x , y , 1 ) 。 ins(x,y,1)。 ins(x,y,1)

至于另一个 x ≤ y x≤y xy,同理 i n s ( y , x , 0 ) ins(y,x,0) ins(y,x,0)

最后再缩点后跑最短路,在强联通分量中取最大值+1,统计答案。
具体证明看: P o P o Q Q Q PoPoQQQ PoPoQQQ大佬的证明

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define _ 300010
using namespace std;
	int n,m1,m2,len=0,ans=0;
	int dis[610][610];
	int last[_],belong[_],low[_],dfn[_],sta[_],ma[_];
	bool insta[_];
	struct node{int x,y,next;} a[_];
void ins(int x,int y)
{
	a[++len]=(node){x,y,last[x]}; last[x]=len;
}
int id=0,tp=0,cnt=0;
void tarjan(int x)
{
	low[x]=dfn[x]=++id;
	sta[++tp]=x;
	insta[x]=true;
	for(int i=last[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(dfn[y]==-1)
		{
			tarjan(y);
			if(low[x]>low[y]) low[x]=low[y];
		}
		else if(insta[y]&&low[x]>dfn[y]) low[x]=dfn[y];
	}
	if(low[x]==dfn[x])
	{
		int i;
		cnt++;
		do{
			i=sta[tp--];
			insta[i]=false;
			belong[i]=cnt;
		}while(i!=x);
	}
}
void work()
{
	memset(dfn,-1,sizeof(dfn));
	memset(insta,false,sizeof(insta));
	for(int i=1;i<=n;i++)
		if(dfn[i]==-1) tarjan(i);
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			if(belong[i]==belong[k])
				for(int j=1;j<=n;j++)
					if(belong[i]==belong[j]&&belong[j]==belong[k])
						dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	for(int i=1;i<=n;i++)
		if(dis[i][i]) {printf("NIE");return;}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(belong[i]==belong[j]) ma[belong[i]]=max(ma[belong[i]],abs(dis[i][j]));
	for(int i=1;i<=cnt;i++)
		ans+=ma[i]+1;
	printf("%d",ans);
}
int main()
{
	int x,y;
	scanf("%d %d %d",&n,&m1,&m2);
	memset(dis,63,sizeof(dis));
	for(int i=1;i<=n;i++)
		dis[i][i]=0;
	for(int i=1;i<=m1;i++)
	{
		scanf("%d %d",&x,&y);
		ins(x,y),ins(y,x);
		dis[x][y]=min(dis[x][y],1);
		dis[y][x]=min(dis[y][x],-1);
	}
	for(int i=1;i<=m2;i++)
	{
		scanf("%d %d",&x,&y);
		ins(y,x);
		dis[y][x]=min(dis[y][x],0);
	}
	work();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值