首页 > 其他分享 >CF-613-D

CF-613-D

时间:2024-01-13 21:59:34浏览次数:33  
标签:back sz ne int 613 CF st DP

613-D 题目大意

给定一颗\(n\)个节点的树。

\(q\)组询问,每组询问给定\(k\)个点,问至少要删除树中多少个点才能使这\(k\)个点两两不连通,无解则输出\(-1\)。

这里\(\sum{k_i}\)的规模大致和\(n\)相当。


Solution

虚树模板题。

暴力的做法是每组询问都对整棵树进行遍树形DP,复杂度为\(O(qn)\)。虚树的做法是对于每组询问的\(k\)个点,在原树的基础上重构一颗小规模的新树,在新树上进行树形DP,虚树能够保证重构树中的节点数量不超过\(2k\),时间复杂度为\(O(\sum{k_i}·log\sum{k_i})\)。

对于一组点集,虚树的构建过程如下:
1、预处理出原树的\(dfs\)序,对点集按\(dfn\)序升序排序。
2、用栈\(st\)来维护\(dfs\)访问过的点,栈中的点从栈底到栈顶构成一条链,深度从小到大。首先加入根节点0。
3、现在考虑加入点\(x\),设\(y=st[top]\),\(z=lca(x,y)\),分情况讨论:
\(\;\;\;\;\)①当\(z=y\)时,说明\(x\)是\(y\)子树内的节点,此时直接把\(x\)加入栈中即可。
\(\;\;\;\;\)②当\(z\ne y\)时,说明\(x\)不是\(y\)子树内的节点,此时应当把栈中深度大于\(z\)的节点全部出栈,在出栈的过程中进行连边建立虚树,直到\(dep[st[top-1]]<dep[z]\)为止。最后栈顶元素的深度仍然不小于\(z\)的深度,这时分两种情况\(\;\;\;\;\)讨论:
\(\;\;\;\;\;\;\;\;\)\(Ⅰ.\)如果\(y=z\),说明\(lca\)已在栈中,直接把\(x\)入栈即可。
\(\;\;\;\;\;\;\;\;\)\(Ⅱ.\)如果\(y\ne z\),说明\(lca\)不在栈中,那么把\(z\)向\(y\)连一边条,再把\(y\)出栈,然后再把\(z\)和\(x\)先后入栈。
4、枚举完点集之后,栈中仍然保存着一条链,把栈中节点全部出栈,并进行连边。

树形DP:
剩余的就是如何进行树形DP,首先判定无解的情况,存在一组父子点都在询问给的点集之中,此时无解。
对整棵树进行DP,维护一个\(sz\)值,询问点为\(1\),其余点为\(0\),分情况讨论:
1、如果当前点\(x\)是询问点,对它的所有儿子\(y\)进行\(dfs\),如果儿子\(y\)的\(sz\)值为正,说明要从\(x\)到\(y\)的路径上删除一个点,否则不需要操作。
2、如果当前点\(x\)不是询问点,对它的所有儿子\(y\)进行\(dfs\),并把所有儿子的\(sz\)值累加到\(x\)的\(sz\)值中,如果最终\(sz[x]\)超过1,说明删除这个非询问点,能够隔离开它子树中所有的询问点,否则不需要操作。

要注意的是需要在递归结束时清空相应的\(sz\)值,以及把新加的边都删去。如果整个操作完再去统一初始化,则时间复杂度会退化到\(O(qn)\)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
	ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
	int P=19;
	vector<vector<int>> e(n),ne(n);
	vector<vector<int>> fa(n,vector<int>(P));
	vector<int> dfn(n),dep(n);
	int idx=0;
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		--x,--y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	function<void(int,int,int)> dfs=[&](int x,int father,int depth){
		dfn[x]=++idx,fa[x][0]=father,dep[x]=depth;
		for(int i=1;i<P;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
		for(auto y:e[x]){
			if(y==father) continue;
			dfs(y,x,depth+1);
		}
	};
	function<int(int,int)> lca=[&](int x,int y){
		if(dep[x]<dep[y]) swap(x,y);
		for(int i=P-1;~i;i--){
			if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
		}
		if(x==y) return x;
		for(int i=P-1;~i;i--){
			if(fa[x][i]!=fa[y][i]){
				x=fa[x][i],y=fa[y][i];
			}
		}
		return fa[x][0];
	};
	dfs(0,0,0);
	int q;
	cin>>q;
	vector<int> sz(n);
	int ans=0;
	function<void(vector<int>&)> build=[&](vector<int> &p){
		sort(p.begin(),p.end(),[&](const auto &x,const auto &y){
			return dfn[x]<dfn[y];
		});
		vector<int> st{0};//根节点入栈
		if(p[0]!=0) st.push_back(p[0]);//第一个查询点入栈
		for(int i=1;i<p.size();i++){
			int x=p[i],y=st.back();
			st.pop_back();
			int z=lca(x,y);
			while(st.size()&&dep[st.back()]>=dep[z]){//对当前链连边
				ne[st.back()].push_back(y);
				y=st.back();
				st.pop_back();
			}
			if(z!=y){//对lca和栈顶点连边,栈顶点出栈,lca入栈
				ne[z].push_back(y);
			}
			st.push_back(z);
			st.push_back(p[i]);
		}
		while(st.size()>1){///对最后一条链连边
			int y=st.back();
			st.pop_back();
			ne[st.back()].push_back(y);
		}
	};
	function<void(int)> DP=[&](int x){
		if(sz[x]){
			for(auto y:ne[x]){
				DP(y);
				if(sz[y]) ans++,sz[y]=0;
			}
		}else{
			for(auto y:ne[x]){
				DP(y);
				sz[x]+=sz[y],sz[y]=0;
			}
			if(sz[x]>1) ans++,sz[x]=0;
		}
		ne[x].clear();
	};
	while(q--){
		int k;
		cin>>k;
		vector<int> p(k);
		for(int i=0;i<k;i++){
			cin>>p[i];
			p[i]--;
			sz[p[i]]=1;
		}
		int flag=0;
		for(int i=0;i<k;i++){
			if(p[i]!=0&&sz[fa[p[i]][0]]) flag=1;
		}
		if(flag){
			for(int i=0;i<k;i++) sz[p[i]]=0;
			cout<<-1<<'\n';
			continue;
		}
		build(p);
		ans=0;
		DP(0);
		sz[0]=0;
		cout<<ans<<'\n';
	}
	return 0;
}

标签:back,sz,ne,int,613,CF,st,DP
From: https://www.cnblogs.com/fengxue-K/p/17963015

相关文章

  • CF1201C - Maximum Median
    思路二分答案。对于一个mid,查询中位数要是为mid的话至少要做多少次操作,最小操作次数就是排序后从中位数开始计算max(0,mid-v[i])的和ac代码#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;consti64inf=8e18;typedefpair<int,int>pii;cons......
  • CF1876D Lexichromatography
    CF1876DLexichromatographyLuoguCF1876D题目描述给定一个长为\(n\)的序列\(a\),你需要对这个序列进行红蓝染色。染色有如下要求:每个位置恰好染上其中一种颜色。对于所有的值\(k\),在这个序列的任意子区间\([l,r]\)中,值为\(k\)且为红色的位置数减去值为\(k\)且为......
  • CF713D 题解
    题意给一个\(01\)矩阵,多次求在给定区间内最大的全\(1\)正方形边长。思路容易想到二分:先预处理出以每个位置为右下角的最大合法正方形的边长\(mx_{i,j}\),然后对于每个询问,我们二分边长\(mid\),设当前询问的区间左上角为\((x_1,y_1)\),右下角为\((x_2,y_2)\),则能取到\(mi......
  • AT_cf17_final_j 题解
    题意给定一棵既有点权也有边权的树,构造一个完全图,图中两点间边的边权为树中两点点权之和加上两点间的距离,求该图的最小生成树。思路发现完全图总边数太大,考虑减少边数。这里有一个性质:如果在一个图中选取任意个联通的边集,使得它们的并为全集,则整个图的最小生成树中的边一定在......
  • CF1284E New Year and Castle Construction
    NewYearandCastleConstructionLuoguCF1284E题目描述给定大小为\(N\)的点集\(S\)。保证点集中的任意三点不共线,且不存在重复的点。设\(f(p)\)表示满足如下条件的\(S\)的四元子集\(T\)的个数:\(T\subsetS\\landp\notinT\)\(T\)中的元素能组成一个四边形,......
  • 【五期李伟平】CCF-A(AAAI'21)Game of Gradients: Mitigating Irrelevant Clients in Fe
    Nagalapatti,Lokesh,andR.Narayanam."GameofGradients:MitigatingIrrelevantClientsinFederatedLearning."(2021).  针对联邦学习中相关客户端选择(FRCS)的问题,本文提出一种可以选择具有相关数据的客户端的方法,并提出一个检测拥有特定目标标签数据的客户端......
  • CF-87-D
    87-D题目大意给定一颗\(n\)个节点的树,边带权。现在要枚举所有路径,对于一条路径,取边权最大的边给它得分加\(1\);若有多个权最大的边,则这些边的得分都加\(1\)。输出最后所有边中的最大的得分是多少,有多少条得分最大的边。第二行输出这些得分最大的边的编号Solution首先考虑简......
  • rhel配置ACFS集群文件系统
    文档课题:rhel配置ACFS集群文件系统.环境介绍:OS:rhel7.964位架构:rac双节点数据库:oracle11.2.0.41、配置前信息如下所示,在安装好p31718723_112040_Linux-x86-64.zip补丁后,asmclusterfilesystems和volumes选项卡正常显示,当前已添加磁盘组ACFS,现在目标是创建ASM集群文件系......
  • CF1006E Military Problem 题解
    CF1006EMilitaryProblem题解题意给定一颗有\(n\thinspace(2\leqn\leq2\times10^5)\)个节点的树,树根为\(1\)。对于每个节点\(i\thinspace(2\leqi\leqn)\)都有它的父节点\(p_i\),并且每个节点的子节点都是按从小到大的顺序排列的的。有\(q\thinspace(1......
  • CF455A补题
    思路取与不取的问题,用dp就行ac代码#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;consti64inf=8e18;typedefpair<int,int>pii;constintN=1e5+10;i64dp[N];voidsolve(){intn;cin>>n;map<int,in......