糖果-差分约束
题目描述
题解
典型的差分约束;
我们不妨假设边u→vu→vu→v表示的是vvv比uuu大多少,贪心的想到要使得最后的糖果数最小,就尽可能的使得相连的两点糖果数差值尽可能的小(一定是以两者间小的为标准,相等时差为000,否则大的数比小的至少大111),最后的糖果总数显然最大。
于是我们针对这5种情况分别建边(以下出现的siz[x]siz[x]siz[x]表示的是xxx的糖果数):
1、当条件为siz[u]==siz[v]siz[u]==siz[v]siz[u]==siz[v],则建边w[u,v]=0;w[v,u]=0w[u,v]=0;w[v,u]=0w[u,v]=0;w[v,u]=0表示siz[u]==siz[v]siz[u]==siz[v]siz[u]==siz[v])
2、当条件为siz[u]<siz[v]siz[u]<siz[v]siz[u]<siz[v],若u==vu==vu==v则直接输出−1−1−1(显然不成立),否则建边w[u,v]=1w[u,v]=1w[u,v]=1(表示siz[v]比siz[u]大1siz[v]比siz[u]大1siz[v]比siz[u]大1)
3、当条件为siz[u]>=siz[v]siz[u]>=siz[v]siz[u]>=siz[v],则建边w[v,u]=0w[v,u]=0w[v,u]=0(表示siz[u]==siz[v]siz[u]==siz[v]siz[u]==siz[v],注意方向v→uv→uv→u,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等)
4、当条件为siz[u]>siz[v]siz[u]>siz[v]siz[u]>siz[v],若u==vu==vu==v则直接输出−1−1−1(显然不成立),否则建边w[v,u]=1w[v,u]=1w[v,u]=1(表示siz[u]比siz[v]大1siz[u]比siz[v]大1siz[u]比siz[v]大1)
5、当条件为siz[u]<=siz[v]siz[u]<=siz[v]siz[u]<=siz[v],则建边w[u,v]=0w[u,v]=0w[u,v]=0(表示siz[v]==siz[u]siz[v]==siz[u]siz[v]==siz[u],注意方向u→vu→vu→v,因为要保证最优性,就必须从小的向大的转移,尽可能的让大的和小的相等
接着,新建一个000节点作为源点,向i=1 ni=1~ni=1 n所有点都连边w[0,i]=1w[0,i]=1w[0,i]=1(表示每个点至少有1个糖果)(倒序建边,否则spfaspfaspfa会TTT)
然后,我们跑一遍最长路
代码实现
#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的题,可以考虑倒序建边。