图论专题
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 间谍网络
求强连通分量,缩点,每个连通块维护最小值,最后没有入度的点存起来。