BZOJ2330 糖果[差分约束方案+spfa?/tarjan]
以往对于差分约束理解不是太深,导致这题屡次被坑,在此记录一下细节的理解。
差分约束实际上就是利用了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]的更多相关文章
- bzoj2330糖果——差分约束
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2330 差分约束,再建立一个源点0,向所有点连边权为1的边,表示每个人都会分到糖果: 答案较大 ...
- 【BZOJ2330】【SDOI2012】糖果(差分约束,SPFA)
[BZOJ2330][SDOI2012]糖果 题面 题目描述 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要 ...
- BZOJ2330:[SCOI2011]糖果(差分约束)
Description 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的 ...
- bzoj2330(差分约束)
题解:这道题是练差分约束的一道好题目吧,我具体在代码中注释,这样更加好理解, 为什么求最长路呢?因为这样保证了满足条件,如果存在正权环,就表示无解,就是 正权环之间不断要更多的糖果才行. #inclu ...
- P3275 [SCOI2011]糖果 && 差分约束(二)
学习完了差分约束是否有解, 现在我们学习求解最大解和最小解 首先我们回想一下是否有解的求解过程, 不难发现最后跑出来任意两点的最短路关系即为这两元素的最短路关系. 即: 最后的最短路蕴含了所有元素之间 ...
- BZOJ 2330 SCOI2011糖果 差分约束
2330: [SCOI2011]糖果 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2819 Solved: 820 题目连接 http://www ...
- bzoj 2330 [SCOI2011]糖果 差分约束模板
题目大意 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配 ...
- 洛谷P3275 [SCOI2011]糖果(差分约束)
题目描述 幼儿园里有 $N$ 个小朋友,$lxhgww $老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的 ...
- POJ 2983 Is the Information Reliable? 信息可靠吗 (差分约束,spfa)
题意:有n个站排成一列,针对每个站的位置与距离关系,现有多个约束条件,约束条件分两种:(1)确定的.明确说明站a距离站b多少个单位距离.(2)不确定的.只知道a在b的左边至少1个单位距离. 根据已知 ...
随机推荐
- 数据库工具DbVisualize安装、破解教程,亲测可用
之前工作中遇到生产环境不允许导入Oracle的dmp文件,只能导入sql脚本,但是表中存在clob字段,直接用plsql工具无法导出clob字段,用了下dbvisualizer可以直接导出,亲测可用. ...
- css的优先级权重
选择器(优先级从高到低) 示例 特殊性值 !important(重要性标识) div { color: #fff !important; } 无,但为了方便记忆,可将其表示为1,0,0,0,0 行内样 ...
- 移动端1px的边框
我们知道,在移动端存在物理像素(physical pixel)和设备独立像素(density-independent pixel)的概念.物理像素也称为设备像素,它是显示设备中一个最微小的物理部件,每 ...
- Lucky Sorting(CodeForces-109D)【思维】
题意:给出一组数,要求从小到大排序,并且排序的过程中,发生交换的两个数至少一个为幸运数(十进制位均为4或7),问能否在(2×n)次交换内完成排序,如果能,输出交换的方案(不要求步骤数最少). 思路:首 ...
- thinkphp命令行生成模型类
thinkphp命令行生成模型类 当你需要创建大量的模型类的时候,不妨考虑下命令行生成,可以快速创建模型类. 在windows下面,使用Win+R输入cmd进入命令控制台,切换到项目根目录(也就是th ...
- X86逆向6:易语言程序的DIY
易语言程序在中国的用户量还是很大的,广泛用于外挂的开发,和一些小工具的编写,今天我们就来看下如何给易语言程序DIY,这里是用的易语言演示,当然这门技术也是可以应用到任何一门编译型语言中的,只要掌握合适 ...
- C# 不用添加WebService引用,调用WebService方法
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行. [System.Web.Script.Services.ScriptService] 使用HttpWeb ...
- VS2008提示无法打开包括文件:“afxcontrolbars.h”解决办法
主要造成这个问题的主要原因是,在打了SP1的VS2008下创建的项目放在没有打SP1的VS2008下就会报错 解决办法: 一.给VS2008打上SP1补丁即可 此方法比较彻底,但是安装较 ...
- Django-DRF-视图的演变(二)
Django-DRF-视图的演变 版本一(基于类视图APIView类) views.py: APIView是继承的Django View视图的. 1 from .serializers impor ...
- java实现spark常用算子之Sample
import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.a ...