191014差分约束和强连通分量练习

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值