2019百度之星初赛4 部分题解

题目链接

(已被hack)

Totori’s Switching Game(全局最小割)


题意:

给一个无向图,可能有重边,问是否可以找到边不相同的 k k 个生成树。

思路:

比赛的时候直接判断了所有点的度,实际上是因为数据太水过了。
后来才知道这题是全局最小割的板题。全局最小割可以求出使得无向图不连通的最小代价,那么我们将每一条边的权值设为1,那么如果当前的全局最小割为 m i n c u t mincut ,如果它等于 k k ,就说明要断开 k k 条边才能使得图不连通,也就是说这张图存在 k k 个生成树。
那么就套一下板子即可。

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=400;
int tu[N][N],dis[N];
bool vis[N],d[N];
int StoerWagner(int n)//顶点数
{
    int p=n,ans=inf,Max,t,s,k,i;
    memset(d,0,sizeof(d));
    while (--p>0)
    {
        memset(vis,0,sizeof(vis));
        memset(dis,0,sizeof(dis));
        i=1;
        while (d[i]) i++;
        vis[i]=1;
        for (int j=1;j<=n;j++)
            if (!d[j] && !vis[j])
                dis[j]=tu[i][j];
        t=s=i;
        for (;i<=n;i++)
        {
            Max=0;
            for (int j=1;j<=N;j++)
                if (!d[j] && !vis[j] && Max<dis[j])
                    Max=dis[k=j];
            if (!Max) break;
            vis[k]=1;
            for (int j=1;j<=N;j++)
                if (!d[j] && !vis[j])
                    dis[j]+=tu[k][j];
            s=t;
            t=k;
        }
        if (ans>dis[t]) ans=dis[t];
        d[t]=1;
        for (int j=1;j<=N;j++)
            if (!d[j])
            {
                tu[s][j]+=tu[t][j];
                tu[j][s]+=tu[j][t];
            }
    }
    return ans;
}
int n,m,K;
int T;
int main(){
    scanf("%d",&T);
    while(T--){
        memset(tu,0,sizeof(tu));
        scanf("%d%d%d",&n,&m,&K);
        for(int i=1,u,v;i<=m;i++){
            scanf("%d%d",&u,&v);
            tu[u][v]++;tu[v][u]++;
        }
        if(StoerWagner(n)>=K){
            puts("Yes");
        }else puts("No");
    }
}

题目链接

wls 的树(树链剖分+主席树+并查集)


题意:

给定一颗树,有 q q 次操作,两种类型操作,第一种 1   x 1 \ x ,将以 x x 为根的子树重新排列按照编号从大到小变成一条链,编号最大的做为根。第二种 2   x   y 2 \ x \ y ,查询 x x y y 的路径上经过几条边。

思路:

对于第一种操作,可以用并查集维护,将 x x 的子树的点都指向 x x ,在每次操作时观察该点是否已经使用过第一种操作,如果使用过,就将它指向 x x ,那么它的子树必然也指向 x x ,这样平均每一个点只用操作一次。
对于第二种操作,我们现观察 x x y y 是否在同一个集合内,如果是,那么答案就是他们的差值。如果不是,那么假设在 f 1 f1 f 2 f2 集合中,我们先计算出 f 1 f1 f 2 f2 的距离 d i s = d e e p [ f 1 ] + d e e p [ f 2 ] 2   d e e p [ l c a ( f 1 , f 2 ) ] dis=deep[f1]+deep[f2]-2 \ * deep[lca(f1,f2)] ,再在线段树中算出在 f 1 f1 子树中大于 x x 的个数,在 f 2 f2 子树中大于 y y 的个数,相当于从 x f 1 f 2 y x \to f1 \to f2 \to y ,把三个距离加起来即可。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
vector<int>e[N];
//树剖
int tot[N],son[N],deep[N],fa[N];
int dfs1(int u,int f,int dep){
	tot[u]=1;fa[u]=f;deep[u]=dep;
	int pd=-1;
	for(int v:e[u]){
		if(v==f)continue;
		tot[u]+=dfs1(v,u,dep+1);
		if(tot[v]>pd)pd=tot[v],son[u]=v;
	}
	return tot[u];
}
int idx[N],top[N],id[N],cnt;
void dfs2(int u,int f){
	idx[u]=++cnt,id[cnt]=u;
	top[u]=f;
	if(!son[u])return ;
	dfs2(son[u],f);
	for(int v:e[u]){
		if(!idx[v])dfs2(v,v);
	}
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	return x;
}
//
//主席树
struct zx_tree{
	int ls[N*50],rs[N*50],rt[N*50],tot,sum[N*50];
	inline void update(int &root,int pre,int l,int r,int pos){
		root=++tot;ls[root]=ls[pre],rs[root]=rs[pre],sum[root]=sum[pre]+1;
		if(l==r)return ;
		int mid=(l+r)/2;
		if(pos<=mid)update(ls[root],ls[pre],l,mid,pos);
		else update(rs[root],rs[pre],mid+1,r,pos);
	}
	inline int query(int root,int pre,int l,int r,int k){
		if(l>k)return sum[root]-sum[pre];
		int ans=0;
		int mid=(l+r)/2;
		if(k<mid)ans+=query(ls[root],ls[pre],l,mid,k);
		if(k<r)ans+=query(rs[root],rs[pre],mid+1,r,k);
		return ans;
	}
}T;
//
//并查集
int Fa[N];
int fi(int x){
	if(x==Fa[x])return x;
	return Fa[x]=fi(Fa[x]);
}
//
int TT,n,m;
void init(){
	for(int i=1;i<=n;i++)Fa[i]=0,idx[i]=0,son[i]=0;
	for(int i=1;i<=n;i++)e[i].clear();
	cnt=0;deep[0]=0;
	for(int i=1;i<=T.tot;i++)T.rt[i]=0;
	T.tot=0;
}
void dfs(int u,int x){
	if(Fa[u]!=0){
		Fa[fi(u)]=x;
		return ;
	}
	else{
		Fa[u]=x;
		for(int v:e[u]){
			if(v==fa[u])continue;
			dfs(v,x);
		}
	}
}
int cl2(int x,int y){
	int f1=x,f2=y;
	int d1=0,d2=0;
	if(Fa[x]){
		f1=fi(x);
		d1=T.query(T.rt[idx[f1]+tot[f1]-1],T.rt[idx[f1]-1],1,n,x);
	}
	if(Fa[y]){
		f2=fi(y);
		d2=T.query(T.rt[idx[f2]+tot[f2]-1],T.rt[idx[f2]-1],1,n,y);
	}
	if(f1==f2){
		return abs(d1-d2);
	}
	int Lca=lca(f1,f2);
	int dis=deep[f1]+deep[f2]-2*deep[Lca];
	return dis+d1+d2;
}
int main()
{
	//freopen("txt.in","r",stdin);
	scanf("%d",&TT);
	while(TT--){
		scanf("%d",&n);
		init();
		for(int i=1,u,v;i<n;i++){
			scanf("%d%d",&u,&v);
			e[u].push_back(v);e[v].push_back(u);
		}
		dfs1(1,0,1),dfs2(1,0);
		for(int i=1;i<=cnt;i++)T.update(T.rt[i],T.rt[i-1],1,n,id[i]);
		scanf("%d",&m);
		int op,x,y;
		while(m--){
			scanf("%d",&op);
			if(op==1){
				scanf("%d",&x);
				if(Fa[x]==0)dfs(x,x);
			}
			else {
				scanf("%d%d",&x,&y);
				printf("%d\n",cl2(x,y));
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40400202/article/details/100749741