1.差分约束系统
1.差分约束系统是一种特殊的N元一次不等式组,有很多约束条件,每个约束条件都是由两个变量作差构成的,形如xi-xj <=ck,其中ck是常数,可以是非负数,也可以是负数,我们要解决的是:求一组解x1=a1,x2=a2····xn=an,使所有约束条件满足。
2.我们可以把不等式xi-xj <=ck转化成从结点j向结点i连一条长度为ck的有向边,设dis[0]=0,以0为起点求单源最短路。若存在负环,则无解;否则,xi=dis[i]就是一组解。
3.在一些题中,约束条件形如xi-xj>=ck,可以转化成xj-xi<=-ck;
T1:小K的农场
传送门
其实就是道模板题,按照刚才讲的建边,遇到相等的,就建边权为0的双向边,判断有无负环即可;
代码
#include<bits/stdc++.h>
#define ll long long
#define N 30005
using namespace std;
int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
bool vis[N],mark;
int tot,n,m,x,y,z,t;
int first[N],net[N],to[N],w[N],dis[N];
inline void add(int x,int y,int z)
{
net[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
inline void spfa(int x)
{
if(mark) return;
vis[x]=1;
for(int i=first[x];i;i=net[i])
{
if(mark) return;
int v=to[i];
if(dis[v]>dis[x]+w[i])
{
dis[v]=dis[x]+w[i];
if(vis[v])
{
mark=1;
return;
}
spfa(v);
}
}
vis[x]=0;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=m;i++)
{
t=read();
if(t==3)
{
x=read();
y=read();
add(x,y,0);
add(y,x,0);
}
else
{
x=read();
y=read();
z=read();
if(t==1) add(x,y,-z);
else add(y,x,z);
}
}
for(int i=1;i<=n;i++)
{
spfa(i);
if(mark)break;
}
if(!mark) printf("Yes\n");
else printf("No\n");
}
T2:账本核算
传送门
跟T1差不多,就建边改一下就可以了,可以直接复制粘贴;
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 30005
using namespace std;
int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
bool vis[N],mark;
int tot,n,m,x,y,z,t;
int first[N],net[N],to[N],w[N],dis[N];
inline void add(int x,int y,int z)
{
net[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
inline void spfa(int x)
{
if(mark) return;
vis[x]=1;
for(int i=first[x];i;i=net[i])
{
if(mark) return;
int v=to[i];
if(dis[v]>dis[x]+w[i])
{
dis[v]=dis[x]+w[i];
if(vis[v])
{
mark=1;
return;
}
spfa(v);
}
}
vis[x]=0;
}
int main()
{
int t;
t=read();
while(t--)
{
memset(first,0,sizeof(first));
memset(net,0,sizeof(net));
memset(to,0,sizeof(to));
memset(w,0,sizeof(w));
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
tot=0;
mark=false;
n=read();
m=read();
for(int i=1;i<=m;i++)
{
x=read();
y=read();
z=read();
add(y,x-1,-z);//
add(x-1,y,z);
}
for(int i=1;i<=n;i++)
{
spfa(i);
if(mark) break;
}
if(!mark) printf("true\n");
else printf("false\n");
}
return 0;
}
2.强连通分量:
targin模板:POJ1236(还统计了缩点后入度为0和出度为0的点)
#include<bits/stdc++.h>
#define N 50005
#define M 50005
using namespace std;
int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
bool insta[N];
int n,m,tot,num,a,b,times,top,ans;
int dfn[M],low[M],sta[M],group[M],in[M],out[M];
int first[N],net[M],to[M];
void add(int x,int y)
{
net[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void targin(int x)
{
dfn[x]=low[x]=++times;
sta[++top]=x;
insta[x]=true;
for(int i=first[x];i;i=net[i]){
int v=to[i];
if(!dfn[v]){
targin(v);
low[x]=min(low[x],low[v]);
}
else if(insta[v]) low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x]){
insta[x]=false;
group[x]=++num;
while(sta[top]!=x){
group[sta[top]]=num;
insta[group[top]]=false;
top--;
}
top--;
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
a=read();
while(a!=0) add(i,a),a=read();
}
for(int i=1;i<=n;i++) if(!dfn[i]) targin(i);
for(int i=1;i<=n;i++){
for(int j=first[i];j;j=net[j]){
if(group[i]!=group[to[j]]) in[group[to[j]]]++,out[group[i]]++;
}
}
int u=0;
for(int i=1;i<=num;i++){
if(!in[i]) ans++;
if(!out[i]) u++;
}
if(num==1) printf("1\n0");
else{
printf("%d\n",ans);
printf("%d",max(ans,u));
}
return 0;
}
双连通分量
1.一个图的点(边)连通度的定义为:最小割点集合中的顶点(边)数;
2.如果一个无向连通图的点或边连通度大于1,则称该图是点或边双连通的(意思是任意删去一个最小割点集合中的点,仍是连通的,故为双连通);
3.一个图有割点(割边—桥),当且仅当这个图的点(边)连通度为1;
4.在图G 的所有子图G’中,如果G’是双连通的,则称G’为双连通子图。如果一个双连通子图G’它不是任何一个双连通子图的真子集,则G’为极大双连通子图(双连通分量),点双连通分量又叫做块。
5.错误猜想:两个割点之间的边一定是割边,割边的两个端点一定是割点。
6.边双连通分量一定是点双连通分量,但点双连通分量不一定是边双连通分量;
附巨神yyt笔记:
模板暂无(是我太弱了 );
T1:魔法石
传送门
关键是从起点向末尾连一条边权为0的边,再找双连通分量,若起点,终点在同一双连通分量,且权值大于0,则能到达;反之不能;
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 400005
#define M 700005
using namespace std;
inline int read(){
char ch=getchar();int sum=0;
while(!(ch>='0'&&ch<='9'))ch=getchar();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum;
}
int first[N],net[M],from[M],to[M],w[M];
int dfn[N],low[N],fa[N];
int n,m,tot,times,x,y,z,str,en;
bool visit[N],is[N];
inline void add(int x,int y)
{
net[++tot]=first[x];
first[x]=tot;
from[tot]=x;
to[tot]=y;
}
inline int getfa(int i)
{
return fa[i]==i?i:fa[i]=getfa(fa[i]);
}
inline void targin(int x)
{
dfn[x]=low[x]=++times;
for(int e=first[x];e;e=net[e])
{
if(visit[e>>1]) continue;
visit[e>>1]=1;
int v=to[e];
if(!dfn[v])
{
targin(v);
low[x]=min(low[x],low[v]);
if(low[v]>dfn[x]) fa[v]=v;
else fa[v]=x;
}
low[x]=min(low[x],dfn[v]);
}
}
int main()
{
int t;
t=read();
while(t--)
{
memset(first,0,sizeof(first));
memset(dfn,0,sizeof(dfn));
memset(visit,0,sizeof(visit));
tot=1;
times=0;
n=read();
m=read();
for(int i=1;i<=m;i++)
{
x=read();
y=read();
z=read();
is[i]=z;
add(x,y);
add(y,x);
}
str=read();
en=read();
add(str,en);
add(en,str);
for(int i=1;i<=n;i++) fa[i]=i;
targin(str);
is[m+1]=0;
int ans=0;
if(getfa(str)==getfa(en))
{
for(int i=2;i<=tot;i+=2)
{
if(getfa(from[i])==getfa(to[i])&&getfa(from[i])==getfa(str)) ans+=is[i>>1];
}
if(ans) puts("YES");
else puts("NO");
}
else puts("NO");
}
return 0;
}