其实最开始我打草稿画图的时候觉得这是拓扑排序,但是我太菜了建不来图然后只能去套差分约束。
什么是差分约束呢,形如xi-xj<=m的式子我们可以联想到最短路的式子:dis[v]<=dis[u]+w。
如何求出xi的解,我们最短路跑一遍就行了。值得一提的是,我们可以把xi-xj<=m变形成xj-xi>=-m,这样是不是就像最长路的式子了?
那最短路的解和最长路的解有什么区别呢,最短路求得最大值,最长路求得最小值。
我们既然知道做法了,那之后是不是求最短/长路了?为了避免有点没在图内,我们建立一个源点连接所有点。
这个题因为每个小孩都必须至少要有1个糖果,所以边权为1.
而如果小孩之间的糖果数量关系存在大于或小于,那么就变成xj-xi>=1这类的(为什么是1?因为我们要确保糖果数量最小,每次只比另外的小孩多一个不就满足了尽量小吗)
以下代码
注意 这个题会超市,所以在输入时剪枝,在建图时先建源点的边而且倒序建边。
#include<bits/stdc++.h>
using namespace std;
int n,k;
int head[500010],cnt;
struct e{
int v,next,w;
}edge[500010];
int dis[500010];
long long ans;
void add(int u,int v,int w)
{
edge[++cnt].w=w;
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
int vis[500010],num[500010];
bool spfa()
{
memset(dis,-1,sizeof(dis));
queue<int>q;
dis[0]=0;
q.push(0);
vis[0]=1;
num[0]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].v;
if(dis[y]<dis[x]+edge[i].w)
{
dis[y]=dis[x]+edge[i].w;
if(!vis[y])
{
if(++num[y]>=n)
{
return 0;
}
q.push(y);
vis[y]=1;
}
}
}
}
return 1;
}
int main()
{
cin>>n>>k;
for(int i=n;i>=1;i--)
{
add(0,i,1);
}
for(int i=1;i<=k;i++)
{
int x;
cin>>x;
int a,b;
cin>>a>>b;
if(x==1)
{
add(a,b,0);
add(b,a,0);
}
if(x==2)
{
add(a,b,1);
}
if(x==3)
{
add(b,a,0);
}
if(x==4)
{
add(b,a,1);
}
if(x==5)
{
add(a,b,0);
}
if(x%2==0 && a==b) {
printf("-1\n");
return 0;
}
}
if(!spfa())
printf("-1");
else{
for(int i=1;i<=n;i++)
{
ans+=dis[i];
}
cout<<ans;
}
}