差分约束总结
遇到了一些很不错的差分约束系统的题目 我觉得需要总结一下的样子。
LINK:种树 万壑树参天 千山响杜鹃 差分约束 觉得海星 由于每个位置只能种一棵树和区间的限定我们不难想到 利用前缀和数组来建图。
显然 有条件 dr-dr-1<=1 dr-dr-1>=0 和某些去加问题 dr-dl-1>=cnt 什么 的通过建图 我们发现要构造出来的解其实就是 d数组了。
考虑 一下怎么跑?从哪开始跑 好像没有一个起点的样子 虚设源点 从而进行构解 源点如果为 0的话 好像没有什么约束条件的样子但是源点设为 n+1 那么则有 dn+1-di>=0
这个条件 我们设dn+1=0 构造出来一组负数解 即可 考虑如何满足最小的取值 我觉的是 由于跑的是最短路 所以求出的解必然是一组 可能不符合实际意义但是是最优的解 。
所以我们调整一下即可 全部都减去最小的 最小的显然是dis[1] 由于dis n+1为0 答案就为 disn+1-dis[1] ==-dis[1]了。(我觉得应该没锅
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000010 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum #define zz p<<1 #define yy p<<1|1 #define mod 1000000007 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=30010; int n,flag,ans; int m,len,t,h,minn=INF; int dis[MAXN],vis[MAXN],v[MAXN],q[MAXN<<5]; int lin[MAXN],nex[MAXN<<2],ver[MAXN<<2],e[MAXN<<2]; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void spfa() { while(h++<t) { int x=q[h];vis[x]=0; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { v[tn]=v[x]+1; if(v[tn]>=n){flag=1;return;} dis[tn]=dis[x]+e[i]; if(!vis[tn])q[++t]=tn,vis[tn]=1; } } } } signed main() { //freopen("1.in","r",stdin); n=read();m=read();++n; memset(dis,0x3f,sizeof(dis)); for(int i=1;i<n;++i) { add(i,i+1,1),add(i+1,i,0); add(n,i,0); } for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read()+1;z=read(); add(y,x,-z); } q[++t]=n;dis[n]=0; spfa(); printf("%d\n",-dis[1]); return 0; }
LINK:狡猾的商人 商人 总是 具有高雅的智慧吧。
还是考虑前缀和建图 不过不同的是 某个区间特定的值是有了我们是这样转换的 dr-dl-1>=w dr-dl-1<=w
所以就可以直接跑了 ...
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 #define pii pair<ll,ll> #define mk make_pair #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum #define zz p<<1 #define yy p<<1|1 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=110,maxn=1010; int T,len,t,h; int n,m,flag; int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1]; int vis[MAXN],dis[MAXN],v[MAXN],q[MAXN*MAXN]; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void spfa() { while(h++<t) { int x=q[h];vis[x]=0; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(dis[x]+e[i]<dis[tn]) { dis[tn]=dis[x]+e[i]; v[tn]=v[x]+1; if(v[tn]>=n){flag=1;return;} if(!vis[tn])q[++t]=tn,vis[tn]=1; } } } } int main() { //freopen("1.in","r",stdin); T=read(); while(T--) { flag=0;len=0; memset(lin,0,sizeof(lin)); n=read();m=read();++n; for(int i=1;i<=m;++i) { int l,r,z; l=read();r=read()+1;z=read(); add(l,r,z); add(r,l,-z); } t=h=0; for(int i=1;i<=n;++i) { q[++t]=i; v[i]=dis[i]=vis[i]=0; } spfa(); if(flag)puts("false"); else puts("true"); } return 0; }
LINK:天平 其实在我的眼中天平从来都没有平过..毫无用处的东西...
这题就很神了 很鬼畜的样子 发现差分约束总是 要对建图有一定的要求。
有一堆约束条件 但是可能有未知的砝码 所以最后我们利用spfa构造的只是其中的一种很难完成题目中所说的 确定的情况。
神仙题目 原来跑的是floyd 处理差分约束的关系。可以设两个数字 d[i][j] 表示i和j砝码最多差多少克 b i j 表示i和j砝码最少差多少克。
这个东西可以利用floyd实现 这个东西 然后最后求的话其实就是暴力枚举两个东西 判断即可。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 100000000000000000ll #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 #define mod 1000000007 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=60; int n; int A,B,ans1,ans2,ans3; int d[MAXN][MAXN],b[MAXN][MAXN]; char a[MAXN][MAXN]; int main() { //freopen("1.in","r",stdin); n=read();A=read();B=read(); for(int i=1;i<=n;++i) { scanf("%s",a[i]+1); for(int j=1;j<=n;++j) { if(a[i][j]=='?')d[i][j]=2,b[i][j]=-2; if(a[i][j]=='+')d[i][j]=2,b[i][j]=1; if(a[i][j]=='-')d[i][j]=-1,b[i][j]=-2; if(a[i][j]=='=')d[i][j]=b[i][j]=0; } } for(int k=1;k<=n;++k) for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) { d[i][j]=min(d[i][j],d[i][k]+d[k][j]); b[i][j]=max(b[i][j],b[i][k]+b[k][j]); } for(int i=1;i<=n;++i) { if(i==A||i==B)continue; for(int j=i+1;j<=n;++j) { if(j==A||j==B)continue; if(b[A][i]>d[j][B]||b[B][i]>d[j][A])++ans1; if(b[i][A]>d[B][j]||b[j][A]>d[B][i])++ans3; if((b[A][i]==d[A][i]&&b[j][B]==d[j][B]&&b[A][i]==b[j][B])||(b[A][j]==d[A][j]&&b[i][B]==d[i][B]&&b[A][j]==b[i][B]))++ans2; } } printf("%d %d %d\n",ans1,ans2,ans3); return 0; }