糖果-差分约束
题目描述
题解
典型的差分约束;
我们不妨假设边
u
→
v
u→v
u→v表示的是
v
v
v比
u
u
u大多少,贪心的想到要使得最后的糖果数最小,就尽可能的使得相连的两点糖果数差值尽可能的小(一定是以两者间小的为标准,相等时差为
0
0
0,否则大的数比小的至少大
1
1
1),最后的糖果总数显然最大。
于是我们针对这5种情况分别建边(以下出现的
s
i
z
[
x
]
siz[x]
siz[x]表示的是
x
x
x的糖果数):
1、当条件为
s
i
z
[
u
]
=
=
s
i
z
[
v
]
siz[u]==siz[v]
siz[u]==siz[v],则建边
w
[
u
,
v
]
=
0
;
w
[
v
,
u
]
=
0
w[u,v]=0;w[v,u]=0
w[u,v]=0;w[v,u]=0表示
s
i
z
[
u
]
=
=
s
i
z
[
v
]
siz[u]==siz[v]
siz[u]==siz[v])
2、当条件为
s
i
z
[
u
]
<
s
i
z
[
v
]
siz[u]<siz[v]
siz[u]<siz[v],若
u
=
=
v
u==v
u==v则直接输出
−
1
−1
−1(显然不成立),否则建边
w
[
u
,
v
]
=
1
w[u,v]=1
w[u,v]=1(表示
s
i
z
[
v
]
比
s
i
z
[
u
]
大
1
siz[v]比siz[u]大1
siz[v]比siz[u]大1)
3、当条件为
s
i
z
[
u
]
>
=
s
i
z
[
v
]
siz[u]>=siz[v]
siz[u]>=siz[v],则建边
w
[
v
,
u
]
=
0
w[v,u]=0
w[v,u]=0(表示
s
i
z
[
u
]
=
=
s
i
z
[
v
]
siz[u]==siz[v]
siz[u]==siz[v],注意方向
v
→
u
v→u
v→u,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等)
4、当条件为
s
i
z
[
u
]
>
s
i
z
[
v
]
siz[u]>siz[v]
siz[u]>siz[v],若
u
=
=
v
u==v
u==v则直接输出
−
1
−1
−1(显然不成立),否则建边
w
[
v
,
u
]
=
1
w[v,u]=1
w[v,u]=1(表示
s
i
z
[
u
]
比
s
i
z
[
v
]
大
1
siz[u]比siz[v]大1
siz[u]比siz[v]大1)
5、当条件为
s
i
z
[
u
]
<
=
s
i
z
[
v
]
siz[u]<=siz[v]
siz[u]<=siz[v],则建边
w
[
u
,
v
]
=
0
w[u,v]=0
w[u,v]=0(表示
s
i
z
[
v
]
=
=
s
i
z
[
u
]
siz[v]==siz[u]
siz[v]==siz[u],注意方向
u
→
v
u→v
u→v,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等
接着,新建一个
0
0
0节点作为源点,向
i
=
1
n
i=1~n
i=1 n所有点都连边
w
[
0
,
i
]
=
1
w[0,i]=1
w[0,i]=1(表示每个点至少有1个糖果)(倒序建边,否则
s
p
f
a
spfa
spfa会
T
T
T)
然后,我们跑一遍最长路
代码实现
#include<bits/stdc++.h>//差分约束
#define M 200009
using namespace std;
int nxt[M],first[M],w[M],to[M],tot;
int n,m,vis[M],cnt[M];
long long ans,d[M];
inline int read(){
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
inline void add(int x,int y,int z){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
inline void spfa(){
vis[0]=1,d[0]=0;
queue<int>q;
q.push(0);
while(q.size()){
int u=q.front();
q.pop(),vis[u]=0;
if(cnt[u]==n-1){printf("-1\n"),exit(0);}
cnt[u]++;
for(register int i=first[u];i;i=nxt[i]){
int v=to[i];
if(d[v]<d[u]+w[i]){
d[v]=d[u]+w[i];
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
}
int main(){
int type,x,y;
n=read(),m=read();
for(register int i=1;i<=m;i++){
type=read(),x=read(),y=read();
if(type%2==0&&x==y) return !printf("-1\n");
if(type==1) add(x,y,0),add(y,x,0);
if(type==2) add(x,y,1);
if(type==3) add(y,x,0);
if(type==4) add(y,x,1);
if(type==5) add(x,y,0);
}for(register int i=n;i>=1;i--) add(0,i,1);
spfa();
for(register int i=1;i<=n;i++) ans+=d[i];
printf("%lld\n",ans);
return 0;
}
做题启发
1,对于卡spfa的题,可以考虑倒序建边。