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

1|0正题

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


1|1题目大意

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

1n,q,di105


1|2解题思路

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

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

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

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

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

时间复杂度:O(nlog2n)


1|3code

#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; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15433139.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(35)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示