图论专题

BZOJ 4668: 冷战 并查集

共有两种操作,添加一条边或者询问两点在哪次操作后联通。强制在线。

void merge(int u,int v)
{
	++tot;//写在return前! 
	if(u==v) return;//
	if(siz[u]<siz[v]) swap(u,v);
	fa[v]=u; siz[u]+=siz[v];
	w[v]=tot;
}
void query(int u,int v)
{
	int cr=u,d1=0,d2=0,f1,f2;
	while(fa[cr]!=cr)d1++,cr=fa[cr]; f1=cr;
	cr=v; while(fa[cr]!=cr)d2++,cr=fa[cr]; f2=cr;
	if(f1!=f2){puts("0");ans=0;return;}
	if(d1>d2) swap(u,v),swap(d1,d2);
	ans=0;
	while(d2>d1)
		ans=max(ans,w[v]),v=fa[v],d2--;
	while(u!=v)
		ans=max(ans,max(w[u],w[v])),
		u=fa[u],v=fa[v];
	printf("%d\n",ans);
}
int main()
{
	n=rdn(); m=rdn();
	for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
	for(int i=1,op,u,v;i<=m;i++)
	{
		op=rdn(); u=rdn()^ans; v=rdn()^ans;
		if(!op) merge(find(u),find(v));
		else query(u,v);
	}
	return 0;
}

[POJ 2395] Out of Hay

有N(2-2000)个农场,M(1-10000)条通路连通各个农场,长度不超1e9,要求遍历全部的农场,且每走1单位长度就要消耗一单位水,每到一个农场可以把自己的水充满,求最小的水箱容量。

1≤N,M≤100000

这显然是最小生成树:要求遍历全部的农场,最小的水箱容量

int n,m,ans;
int father[maxn];
struct data{int start,end,dist;}line[maxn];
bool cmp(data a,data b) {return a.dist<b.dist;}
void init(){for(int i=1;i<=n;i++) {father[i]=i;}}
int findfa(int x) {return x==father[x]?x:father[x]=findfa(father[x]);}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&line[i].start,&line[i].end,&line[i].dist);
	}
	sort(line+1,line+1+m,cmp); init();
	for(int i=1;i<=m;i++){
		int j=findfa(line[i].start);
		int k=findfa(line[i].end);
		if(j==k) continue;
		else father[j]=k;
		ans=line[i].dist;
	}
	printf("%d\n",ans);
	return 0;
} 

P5994 [PA2014]Kuglarz

要知道一个区间的奇偶性有两种办法:

1.aii自己问自己

2.aki,aki-1都问

抽象成点与点建边。

定义[l,r]为(l,r]建边,如(0,1],(1,2]我们能推出(0,2],在一个大区间和两个小区间是由二得三,手玩样例可知最后每个点都必须联通,是一棵树,代价最小,即最小生成树。相当于加了个零点,然后顺利排除了每个只询问自己的情况。

当然这个诡异的思路肯定是人工造的,自然的思路的性质不能看出来但是变形一下就能看。

[题解 P5994 【PA2014]Kuglarz】 - 逃离地球 的博客 - 洛谷博客 (luogu.com.cn)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;
int a[N][N],n,m;
struct edge{
	int nxt,to,w;
}e[N*N*2];
int hd[N*N],cnt,dis[N*N],vis[N*N];
long long ans;
void add(int u,int v,int w)
{
	e[++cnt]={hd[u],v,w};
	hd[u]=cnt;
}
int main()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;++ i)
		for(int j = i,w;j <= n;++ j)
	{
		scanf("%d",&w);
		add(i - 1,j,w);add(j,i - 1,w);
	}
	priority_queue<pair<int,int> > q;
	memset(dis,0x3f,sizeof dis);
	q.push(make_pair(0,1));dis[1] = 0;
	while(!q.empty())
	{
		int x=q.top().second;q.pop();
		if(vis[x])  continue;
		vis[x]=1,ans+=dis[x];
		for(int i=hd[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(dis[y]>e[i].w)   dis[y]=e[i].w,q.push(make_pair(-dis[y],y));
		}
	}
	printf("%lld",ans);
	return 0;
}

[BZOJ 3714] Kuglarz

因为到加油站就能加满油了,所以我们只需要满足油箱里的油能开到下一个加油站就行了.

也就是说,我们只需要将a,b两地的路径分成若干段加油站到加油站的路径,

如果最大值小于等于容量C就能到达.

那么,我们就可以把加油站拿出来建一个最小生成树,

其实,我们可以先求出离每个点i最近的加油站w[i]和距离d[j],

然后对于原图中的每条边x,y

如果w[x]!=w[y],就把w[x],w[y]连一条边,

距离就是d[x]+d[y]+wx,y.

而w[i],d[i]怎么求呢?

跑多源SPFA就行啦.

[BZOJ 2654] tree

给定一个n个点、m条边的带权无向图,每条边是黑色或白色。让你求一棵最小权的恰好有K条白色边的生成树。

边权和越大能容纳的边越多,二分答案。

如果我们给所有白色边增加边权,那么所选的白色边一定越来越少(反之同理)。所以我们二分给白色边增加多少边权,跑kruskal,最后再将增加的边权减去即可。

拓扑排序

给出一个N个点M条带边权边的有向图,判断这个图是否存在环?1≤N,M≤10^6

直接拓扑排序,如果拓扑排序不能把所有点都放入拓扑序中,则存在环。

[Haoi2016]食物链

递推。

P1347 排序

每建一次边就toposort,第一种情况略,第二种是有环,即搞出来有一些点没有在最后的队列里,第三种是点都在队列里,但点不够多,这里如果不是前两种就是第三种。

P4306 [JSOI2010]连通数

bitset乱杀[题解 P4306【JSOI2010]连通数 - Qiuly - 洛谷博客 (luogu.com.cn)](https://www.luogu.com.cn/blog/qiuly/solution-p4306)

这里好像是图论专集

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inf 0x3f3f3f3f
#define ri register int
const int N = 2e3 + 5;
bitset<N> f[N];
int ans,n;
char s[N];
int main()
{
    scanf("%d",&n);
    for(ri i = 1;i <= n;++ i)
    {
        scanf("%s",s);
        for(ri x,j = 1;j <= n;++ j)
            if(s[j - 1] == '1' ||i == j)f[i][j] = 1;
    }
    for(ri i = 1;i <= n;++ i)
        for(ri j = 1;j <= n;++ j)
            if(f[i].test(j))f[i] |= f[j];
    for(ri i = 1;i <= n;++ i)
        ans += f[i].count();
    printf("%d\n",ans);
    return 0;
}

P1262 间谍网络

求强连通分量,缩点,每个连通块维护最小值,最后没有入度的点存起来。

posted @ 2022-07-27 20:34  zyc_xianyu  阅读(32)  评论(0编辑  收藏  举报