【洛谷P3275】糖果【差分约束】【负环】
题目大意:
题目链接:https://www.luogu.org/problemnew/show/P3275
有个小朋友和个要求,其中要求的格式如下:
- , 表示第 个小朋友分到的糖果必须和第 个小朋友分到的糖果一样多;
- , 表示第 个小朋友分到的糖果必须少于第 个小朋友分到的糖果;
- , 表示第 个小朋友分到的糖果必须不少于第 个小朋友分到的糖果;
- , 表示第 个小朋友分到的糖果必须多于第 个小朋友分到的糖果;
- , 表示第 个小朋友分到的糖果必须不多于第 个小朋友分到的糖果;
求至少要多少个糖果使得每个小朋友都可以分到糖果并且所有要求都满足。
思路:
这道题的查封约束十分的明显。可以直接列出来:
- 需要满足,为了写成的形式,可以改成且
- 需要满足,也就是说
- 需要满足,直接反过来变成即可
- 需要满足,即,移项得
- 需要满足,不解释了
对于连边方式2和4需要特判,如果那么明显是不成立的。
但是这道题很奇怪,负权+跑最短路+判负环只能拿70分。改成正权+最长路+判正环才可以。
而且第六个点明显是卡了的,会被卡T。其实应该是可以随机连边的,应该可以过掉,但是看到说就可以过就没有随机了。
总之一道神题orz就对了。
代码:
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int N=100010;
const int M=300010;
int n,m,tot,x,y,z;
int head[N],cnt[N];
ll dis[N],ans,minn;
bool vis[N];
struct edge
{
int to,next;
ll dis;
}e[M];
void add(int from,int to,ll dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
bool spfa()
{
memset(dis,0xcf,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(0);
vis[0]=1;
dis[0]=0;
cnt[0]=1;
while (q.size())
{
int u=q.front(),v;
q.pop();
vis[u]=0;
for (int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if (dis[v]<dis[u]+e[i].dis) //最长路
{
dis[v]=dis[u]+e[i].dis;
cnt[v]=cnt[u]+1;
if (cnt[v]>n+1) return 0;
if (!vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
return 1;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&z,&x,&y);
if (z%2==0&&x==y) return !printf("-1"); //特判,我大于我自己
if (z==1) add(x,y,0),add(y,x,0);
if (z==2) add(x,y,1);
if (z==3) add(y,x,0);
if (z==4) add(y,x,1);
if (z==5) add(x,y,0);
}
for (int i=n;i>=1;i--)
add(0,i,0);
minn=2147483647;
if (spfa())
{
for (int i=1;i<=n;i++)
{
minn=min(minn,dis[i]);
ans+=dis[i];
}
printf("%lld\n",ans-(minn-1)*(ll)n);
}
else printf("-1");
return 0;
}