以往对于差分约束理解不是太深,导致这题屡次被坑,在此记录一下细节的理解。


差分约束实际上就是利用了spfa的一个特性:只要有$dis_y>dis_x+w_{x,y}$就松弛,直到所有边关系都满足$dis_y\le dis_x+w_{x,y}$,而这一不等式恰好可以套在差分约束问题里。差分约束要求满足前面这个玩意,把每个变量的值看做$dis_i$,建边,跑spfa最短路,使得所有边都满足关系。这个之前就已经明白了。

但是忽视了一点:注意到跑出来的最短路中$dis$即为$x$的一组合法取值,并且这组取值同时加上某个值$a$,也是满足不等式约束的。不过,这些解集构成的集合,并不是解集集合的全集(有点绕),显然在跑spfa时只是使得每个点的$dis$贴着松弛他的$dis+w$,其实这个实际取值$x$还可以更小。而如果用最长路的建边方式,$x$可以更大。两者都是等效的。在这题里面,由于要求每个数都是正整数且总和最小,并不可以使用建负权边跑最短路的方法。。。因为解出来的解集即使全部平移到正数区域,你也不知道有没有哪些数可以更小一些,哪些数不能再小了。而建正权边跑最长路,根据上述分析,每个$x$也就是$dis$必然是贴着最小的可能取值的,这样就极其简便的获取最小解。

第二个点,因为spfa理论复杂度$O(mn)$,所以想卡很容易,再加上这题由于差分约束要把所有点都加入队列,所以很容易想到如果构造一条链,连边全部反过来,$1\leftarrow 2\leftarrow ...\leftarrow n$,显然会跑接近$n^2$次,不过数据里并没有故意卡,倒是有一个顺着正向的链的data,我这里采用直接顺着加入队列就没有问题了。但是实际上还是可以卡的?另外,有的点明显spfa被卡,但是有数据连了-1的自环,可以提前特判结束。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=1e5+;
struct thxorz{int to,nxt,w;}G[N<<];
int Head[N],tot,inq[N],rel[N];
int n,m;
ll ans;
inline void Addedge(int x,int y,int z){G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z;}
queue<int> q;
ll dis[N];
#define y G[j].to
inline bool spfa(){
for(register int i=;i<=n;++i)q.push(i),inq[i]=,dis[i]=;
while(!q.empty()){
int x=q.front();q.pop();inq[x]=;//dbg2(x,dis[x]);
for(register int j=Head[x];j;j=G[j].nxt)if(MAX(dis[y],dis[x]+G[j].w)){
rel[y]=rel[x]+;if(rel[y]>=n)return ;
if(!inq[y])inq[y]=,q.push(y);
}
}
return ;
}
#undef y
int main(){//freopen("1.in","r",stdin);//freopen("test.ans","w",stdout);
read(n),read(m);
for(register int i=,opt,x,y;i<=m;++i){
read(opt),read(x),read(y);
if(opt==)Addedge(x,y,),Addedge(y,x,);
else if(opt==)Addedge(x,y,);
else if(opt==)Addedge(y,x,);
else if(opt==)Addedge(y,x,);
else Addedge(x,y,);
if((opt==||opt==)&&x==y){printf("-1\n");return ;}
}
if(spfa()){
for(register int i=;i<=n;++i)ans+=dis[i];
printf("%lld\n",ans);
}
else puts("-1");
return ;
}

2019.11.01UPD:

关于正权图或者负权图的差分约束有更快的线性做法。

以最长路为例,如果图上只有正权边和零边,那么,只要看有没有环是正环,也就是说,对于每一条正边,如果他在一个SCC里,那显然就是正环了,直接判无解。否则,有环的话也只能是零环,零环意味着环上所有点值相同(因为一个被更新,所有点最长路顺着一圈都被更新),所以可以把他们缩掉,这样就变成了一张DAG,这时候就可以直接拓扑排序更新最大值了,最后每个点所在SCC的值就是他的解了。复杂度线性。当然,环上有正有负就不好整了。spfa又死了。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=1e5+;
int n,m;
ll ans;
struct thxorz{
int head[N],nxt[N<<],to[N<<],from[N<<],w[N<<],tot;
inline void add(int x,int y,int z){to[++tot]=y,from[tot]=x,nxt[tot]=head[x],head[x]=tot,w[tot]=z;}
}G1,G2;
int dfn[N],low[N],stk[N],instk[N],bel[N],scc,tim,top;
#define y G1.to[j]
void tarjan(int x){
dfn[x]=low[x]=++tim,stk[++top]=x,instk[x]=;
for(register int j=G1.head[x];j;j=G1.nxt[j]){
if(!dfn[y])tarjan(y),MIN(low[x],low[y]);
else if(instk[y])MIN(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
int tmp;++scc;
do instk[tmp=stk[top--]]=,bel[tmp]=scc;while(tmp^x);
}
}
#undef y
queue<int> q;
int dis[N],deg[N];
#define y G2.to[j]
inline void topo(){
for(register int i=;i<=scc;++i){dis[i]=;if(!deg[i])q.push(i);}
while(!q.empty()){
int x=q.front();q.pop();
for(register int j=G2.head[x];j;j=G2.nxt[j]){
MAX(dis[y],dis[x]+G2.w[j]);
if(!(--deg[y]))q.push(y);
}
}
}
#undef y
int main(){//freopen("1.in","r",stdin);//freopen("test.ans","w",stdout);
read(n),read(m);
for(register int i=,opt,x,y;i<=m;++i){
read(opt),read(x),read(y);
if(opt==)G1.add(x,y,),G1.add(y,x,);
else if(opt==)G1.add(x,y,);
else if(opt==)G1.add(y,x,);
else if(opt==)G1.add(y,x,);
else G1.add(x,y,);
}
for(register int i=;i<=n;++i)if(!dfn[i])tarjan(i);
for(register int t=;t<=G1.tot;++t){
int x=G1.from[t],y=G1.to[t];
if(bel[x]==bel[y]&&G1.w[t]){puts("-1");return ;}
else if(bel[x]^bel[y])G2.add(bel[x],bel[y],G1.w[t]),++deg[bel[y]];
}
topo();
for(register int i=;i<=n;++i)ans+=dis[bel[i]];
printf("%lld\n",ans);
return ;
}

tarjan!

BZOJ2330 糖果[差分约束方案+spfa?/tarjan]的更多相关文章

  1. bzoj2330糖果——差分约束

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2330 差分约束,再建立一个源点0,向所有点连边权为1的边,表示每个人都会分到糖果: 答案较大 ...

  2. 【BZOJ2330】【SDOI2012】糖果(差分约束,SPFA)

    [BZOJ2330][SDOI2012]糖果 题面 题目描述 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要 ...

  3. BZOJ2330:[SCOI2011]糖果(差分约束)

    Description 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的 ...

  4. bzoj2330(差分约束)

    题解:这道题是练差分约束的一道好题目吧,我具体在代码中注释,这样更加好理解, 为什么求最长路呢?因为这样保证了满足条件,如果存在正权环,就表示无解,就是 正权环之间不断要更多的糖果才行. #inclu ...

  5. P3275 [SCOI2011]糖果 && 差分约束(二)

    学习完了差分约束是否有解, 现在我们学习求解最大解和最小解 首先我们回想一下是否有解的求解过程, 不难发现最后跑出来任意两点的最短路关系即为这两元素的最短路关系. 即: 最后的最短路蕴含了所有元素之间 ...

  6. BZOJ 2330 SCOI2011糖果 差分约束

    2330: [SCOI2011]糖果 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2819  Solved: 820 题目连接 http://www ...

  7. bzoj 2330 [SCOI2011]糖果 差分约束模板

    题目大意 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配 ...

  8. 洛谷P3275 [SCOI2011]糖果(差分约束)

    题目描述 幼儿园里有 $N$ 个小朋友,$lxhgww $老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的 ...

  9. POJ 2983 Is the Information Reliable? 信息可靠吗 (差分约束,spfa)

    题意:有n个站排成一列,针对每个站的位置与距离关系,现有多个约束条件,约束条件分两种:(1)确定的.明确说明站a距离站b多少个单位距离.(2)不确定的.只知道a在b的左边至少1个单位距离.  根据已知 ...

随机推荐

  1. Vuecli 3.0 项目自定义添加静态目录,支持在index.html引入

    参考链接:https://blog.csdn.net/qq_15253407/article/details/89491255

  2. alertmanager + federate - Prometheus outside k8s cluster + 总体架构图 对接企业微信告警 + curl alertmanager to send alert

    1.实验的架构 2.k8s 集群外的Prometheus的配置文件 [root@do1cloud03 prometheus]# cat prometheus.yml |egrep -v '#' glo ...

  3. Windows环境下安装Hadoop+Hive的使用案例

    Hadoop安装: 首先到官方下载官网的hadoop2.7.7,链接如下 https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/ 找网盘的 ...

  4. 快速排序的js实现

    该方法的基本思想是: 1.先从数列中取出一个数作为基准数. 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边. 3.再对左右区间重复第二步,直到各区间只有一个数. var ...

  5. SSRF绕过姿势

    0x00 什么是SSRF? SSRF(Server-Side Request Forgery,服务器端请求伪造):是一种由攻击者构造形成由服务器端发起请求的一个漏洞. SSRF 攻击的目标是从外网无法 ...

  6. 11.把文本文件的数据导入到Hive表中

    先在hive里面创建一个表 create table mydb2.t3(id int,name string,age int) row format delimited fields terminat ...

  7. 记一次Sqoop抽数据异常

    1. 环境 Hadoop Sqoop awsEMR 2.8.5 1.4.7 5.26.0 2.错误描述 在使用Sqoop抽取MySQL数据时,使用hdfs作为缓存,s3作为hive的存储地址,命令如下 ...

  8. 剑指offer8:青蛙跳台阶

    1. 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 2. 思路和方法 青蛙每一次跳跃只有两种选择:一是再跳1级阶梯到 ...

  9. LeetCode 160——相交链表(JAVA)

    编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB ...

  10. Sql server 2012 企业中文版安装图文教程

    https://blog.csdn.net/qq_30754565/article/details/82421542