糖果[scoi]

題目鏈接:https://www.luogu.org/problem/P3275

有一堆限值條件然後還要求最值,就有些想到了差分約束。

大於可以轉化成大於等於+1

對於約束a>=b,我們可以連一條邊由b到a,這就意味着b至少比a要大1。

由於每個小朋友都需要一個糖果,我們就擬一個原始起點,讓他連到所有的點的邊都是1,這樣就可以保證每個小朋友都有糖喫。

對於各種約束建成的圖,從原點到每個點的最長路意味着這個小朋友應該拿的糖果數。(之所以是最長路,是我們要保證所有的約束都能被達到。試想2個點之間有多種不同的路徑,而若是取到最長路,定然比任意一條路徑都大或者等於,這樣的話就能保證所有的約束被完成)

最後把所有的dis加加就行了。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=400005;
int n,k;
queue<int> q;int inque[maxn],num[maxn];
int v[maxn],head[maxn],nxt[maxn],w[maxn],dis[maxn];
void add_edge(int x,int y,int z)
{
	static int N=0;N++;
	v[N]=y;w[N]=z;nxt[N]=head[x];head[x]=N;
}
int main() 
{
///	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++)
	{
		int x,a,b;scanf("%d%d%d",&x,&a,&b);
		if(x==1) add_edge(a,b,0),add_edge(b,a,0);
		if(x==2) add_edge(a,b,1);
		if(x==3) add_edge(b,a,0);
		if(x==4) add_edge(b,a,1);
		if(x==5) add_edge(a,b,0);
		//從小的指向大的 
	}
	q.push(0);inque[0]=1;
	dis[0]=0;int s=0;
	for(int i=n;i>=1;i--) add_edge(s,i,1);//每個小朋友至少要一個糖 
	while(!q.empty())
	{
		int x=q.front();q.pop();inque[x]=0;
		num[x]++;
		if(num[x]>=1000)
		{
			printf("-1\n");
			return 0;
		}
		for(int i=head[x];i;i=nxt[i])
		{
			if(dis[v[i]]<dis[x]+w[i])
			{
				dis[v[i]]=dis[x]+w[i];
				if(!inque[v[i]])
				{
					q.push(v[i]);
					inque[v[i]]=1;
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<=n;i++) ans+=dis[i];//dis[i]意味着每個小朋友拿到的糖果數 
	printf("%lld",ans);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章