传送门1
传送门2
传送门3
写在前面:tyvj打卡的不归路and第一次写差分约束,比较生疏……
思路:
1.看到题目给出了元素之间的大小关系,我们比较容易想到了差分约束,而且求的是最少糖果数,所以可以转换成各点到源点最长路径的和(我这里蛋疼了好久,一开始用的是最短路径+奇奇怪怪的方法,只过了70)
2.接下来就是建图了
点与点
①x=y就是不等式组{x-y>=0,y-x>=0},所以我们在x,y中互相连两条长度为0的边即可
②x<y可以转化成y-x>=1,所以连一条x到y的长度为1的边
③x>=y可以转化成x-y>=0,所以连一条y到x的长度为0的边
④x>y可以转化成x-y>=1,所以连一条y到x的长度为1的边
⑤y>=x可以转化成y-x>=0,所以连一条x到y的长度为0的边
关于源点
由于每个人都要分到糖(即最长距离大于0),所以从源点向所有点连一条距离为1的边
3.建图完毕即可跑SPFA求最长路径,与求最短路径类似,只不过dis值不用改,0就行
注意:
1.从源点向外连边时从n到1反着建,不然会莫名T一组
2.操作2,4时判断x,y是否相等,相等即无解,理由同上
3.SPFA中开一个num数组记录每个点进入队列的次数,>=n即是存在负环(别想着用什么√n优化),退出
推荐两篇不错的差分约束入门文章:
http://www.cnblogs.com/void/archive/2011/08/26/2153928.html
http://blog.csdn.net/xuezhongfenfei/article/details/8685313
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int tot,n,m,opr,x,y,start;
int first[100010],num[100010];
LL dis[100010],ans,minn;
bool flag[100010];
queue<int> q;
struct os
{
int fa,son,next,v;
}a[500010];
int in()
{
int f=1,t=0;
char ch=getchar();
while (ch>'9'||ch<'0')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9') t=t*10+ch-'0',ch=getchar();
return f*t;
}
void add(int x,int y,int z)
{
a[++tot].fa=x;
a[tot].son=y;
a[tot].v=z;
a[tot].next=first[x];
first[x]=tot;
}
main()
{
n=in();
m=in();
for (int i=1;i<=m;i++)
{
opr=in();
x=in();
y=in();
if (opr==1)
add(x,y,0),
add(y,x,0);
else if (opr==2)
{
if (x==y) printf("-1"),exit(0);
add(x,y,1);
}
else if (opr==3)
add(y,x,0);
else if (opr==4)
{
if (x==y) printf("-1"),exit(0);
add(y,x,1);
}
else
add(x,y,0);
}
start=n+1;
for (int i=n;i>=1;i--) add(start,i,1);
num[start]=flag[start]=1;
q.push(start);
while (!q.empty())
{
int k=q.front();
flag[k]=0;
q.pop();
for (int i=first[k];i;i=a[i].next)
if (dis[a[i].son]<dis[k]+a[i].v)
{
dis[a[i].son]=dis[k]+a[i].v;
if (!flag[a[i].son])
{
flag[a[i].son]=1;
num[a[i].son]++;
if (num[a[i].son]>=n) printf("-1"),exit(0);
q.push(a[i].son);
}
}
}
for (int i=1;i<=n;i++) ans+=dis[i];
printf("%lld",ans);
}