ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P6805-[CEOI2020]春季大扫除【贪心,树链剖分,线段树】

2021-10-21 14:02:47  阅读:168  来源: 互联网

标签:return 剖分 int void mid 树链 CEOI2020 节点 Change


正题

题目链接:https://www.luogu.com.cn/problem/P6805


题目大意

给出\(n\)个点的一棵树,\(q\)次独立的询问。每次询问会在一些节点上新增一些子节点,然后你每次可以选择两个为选择过的叶子节点然后覆盖它们的路径,要求在覆盖所有边的情况下使得每次的路径长度和最小。

\(1\leq n,q,\sum d_i\leq 10^5\)


解题思路

先考虑暴力怎么做,我们可以把所有叶子去掉然后每个点的权值就是它原来子节点中的叶子数。

然后由于一个节点之间的权值可以两两匹配,贪心的话一个节点只有可能有\(1/2\)条路径延伸到父节点,这样就可以统计了。

然后考虑多个询问如何处理,根据上面的方法,每条边只有可能统计\(1/2\)次,而统计两次时当且仅当子树内能够两两配对,此时因为不能有边没有覆盖,所以就只能拆开一个配对分两个上来。

具体地当一个点的子树中叶子数为偶数时它到其父节点的边会被统计两次。

这个用树链剖分维护即可。

时间复杂度:\(O(n\log^2 n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N=1e5+10;
struct node{
	int to,next;
}a[N<<1];
int n,m,tot,cnt,ls[N],leaf[N],lsz[N];
int dep[N],fa[N],siz[N],son[N],top[N],id[N];
int w[N<<2],v[N<<2],lazy[N<<2];stack<int> s;
void Downdata(int x){
	if(!lazy[x])return;
	lazy[x*2]^=1;lazy[x*2+1]^=1;
	swap(w[x*2],v[x*2]);
	swap(w[x*2+1],v[x*2+1]);
	lazy[x]=0;return;
}
void Build(int x,int L,int R){
	if(L==R){w[x]=(L>1);return;}
	int mid=(L+R)>>1;
	Build(x*2,L,mid);Build(x*2+1,mid+1,R);
	w[x]=w[x*2]+w[x*2+1];
}
void Change(int x,int L,int R,int l,int r){
	if(L==l&&R==r){swap(w[x],v[x]);lazy[x]^=1;return;}
	int mid=(L+R)>>1;Downdata(x);
	if(r<=mid)Change(x*2,L,mid,l,r);
	else if(l>mid) Change(x*2+1,mid+1,R,l,r);
	else Change(x*2,L,mid,l,mid),Change(x*2+1,mid+1,R,mid+1,r);
	w[x]=w[x*2]+w[x*2+1];v[x]=v[x*2]+v[x*2+1];return;
}
void addl(int x,int y){
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;return;
}
void dfs(int x){
	leaf[x]=(a[ls[x]].next==0);
	siz[x]=1;dep[x]=dep[fa[x]]+1;
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa[x])continue;
		fa[y]=x;dfs(y);
		lsz[x]+=lsz[y];siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])son[x]=y;
	}
	lsz[x]+=leaf[x];
	return;
}
void dfs2(int x){
	id[x]=++cnt;
	if(lsz[x]&1)Change(1,1,n,cnt,cnt); 
	if(son[x]){
		top[son[x]]=top[x];
		dfs2(son[x]);
	}
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa[x]||y==son[x])continue;
		top[y]=y;dfs2(y);
	}
	return;
}
void Updata(int x){
	while(x){
		Change(1,1,n,id[top[x]],id[x]);
		x=fa[top[x]];
	}
	return;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		addl(x,y);addl(y,x);
	}
	Build(1,1,n);dfs(1);
	top[1]=1;dfs2(1);
	while(m--){
		int k,x,sum=lsz[1];
		scanf("%d",&k);
		for(int i=1;i<=k;i++){
			scanf("%d",&x);
			if(leaf[x]==1)leaf[x]=2;
			else sum++,Updata(x);
			s.push(x);
		}
		if(sum&1)puts("-1");
		else printf("%d\n",n-1+k+w[1]);
		while(!s.empty()){
			int x=s.top();
			if(leaf[x]==2)leaf[x]=1;
			else Updata(x);s.pop();
		} 
	}
	return 0;
}

标签:return,剖分,int,void,mid,树链,CEOI2020,节点,Change
来源: https://www.cnblogs.com/QuantAsk/p/15433139.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有