[分块][STL][树]【Centroids】不一样的解法

前言

一道好题,也就花了我一个下午而已。

本人做法比较清奇,可以当做开阔思路参考,并不太建议实操(太难调了!)。

文章较啰嗦,谅解。

思路

众所周知,我并不太喜推式子,所以我们考虑直接根据题目限定的条件硬刚。

首先,我们先假定 1 1 1 为根,并求出每个点的子树的大小,记为数组 s i z e size size,并且再维护一下每一个点的时间戳。

接着我们考虑一种特殊情况,即如果 1 1 1 可以为树的重心的情况,那么他就会有接下来的两种子情况:

  • 他所有的儿子的 s i z e size size 全部小于等于 n 2 \dfrac {n}{2} 2n

  • 有一个儿子 x x x s i z e size size 大于 n 2 \dfrac {n}{2} 2n,且这个儿子的子树上一定能找到一个点 y y y 满足 s i z e x − s i z e y ≤ n 2 , s i z e y ≤ n 2 size_x-size_y \le \dfrac{n}{2},size_y\le \dfrac{n}{2} sizexsizey2n,sizey2n。(把这个子树拆下来接到 1 1 1 上就可以满足重心的条件)

第二种情况不是很理解的话可以看下面这张图:

只有两种情况满足其一,那么 1 1 1 就可以成为树的重心。

接下来,我们迁移到一般的情况,那么就需要考虑除了他子树以外的情况。

对于树上的任意节点 x x x,要让他成为树的重心,就会有接下来的三种情况。

  • 他所有的儿子的 s i z e size size 全部小于等于 n 2 \dfrac{n}{2} 2n,并且 n − s i z e x ≤ n 2 n-size_x \le \dfrac{n}{2} nsizex2n

  • 同根节点的第二种情况。

  • n − s i z e x > n 2 n-size_x > \dfrac{n}{2} nsizex>2n,即删除 x x x 后,不属于他子树的那一部分超过了重心的限制。那么在其中,我们又要分为两种情况。首先选择一个不在 x x x 子树上的点 y y y。如果 y y y 不在根节点到 x x x 的那条链上,那么只需要满足 s i z e y ≤ n 2 , n − s i z e x − s i z e y ≤ n 2 size_y \le \dfrac{n}{2},n-size_x-size_y\le \dfrac{n}{2} sizey2n,nsizexsizey2n。否则,我们拆下来的,就是除了 y y y 子树以外的部分,接到 x x x 上面去(毕竟你不可以把 x x x 的子树也拆下来,不然就会没有任何效果。),即满足条件 s i z e y − s i z e z ≤ n 2 , n − s i z e y ≤ n 2 size_y-size_z\le \dfrac{n}{2},n-size_y \le \dfrac{n}{2} sizeysizez2n,nsizey2n,只要找到一个符合条件的 y y y 即可。

那么接下来,问题就转化为了 x x x 能不能找到一个符合条件的 y y y。(毕竟第一个条件很好解决。)

接着继续转化问题,对于情况2,我们可以移一下项,将其转化为 y y y 满足条件 s i z e x − n 2 ≤ s i z e y ≤ n 2 size_x-\dfrac{n}{2} \le size_y \le \dfrac{n}{2} sizex2nsizey2n,且 d f n x ≤ d f n y ≤ d f n x + s i z e x − 1 dfn_x \le dfn_y \le dfn_x+size_x-1 dfnxdfnydfnx+sizex1

对于情况三的第一种情况,我们也可以移一下项,转化为 n − s i z e x − n 2 ≤ s i z e y ≤ n 2 n-size_x-\dfrac{n}{2} \le size_y \le \dfrac{n}{2} nsizex2nsizey2n,且 d f n y < d f n x , d f n y > d f n x + s i z e x − 1 dfn_y<dfn_x,dfn_y>dfn_x+size_x-1 dfny<dfnx,dfny>dfnx+sizex1(对于不在链上这一限制,后面会说怎么处理)。

这两种情况都可以看做有四个常数 p , q , l , r p,q,l,r p,q,l,r,判断是否有对于 x ∈ [ p , q ] , y ∈ [ l , r ] x \in [p,q],y \in [l,r] x[p,q],y[l,r] 存在 x = s i z e y x=size_y x=sizey

显然,我们可以对于 s i z e size size 分块,然后开两个 s e t set set a , b a,b a,b a x a_x ax (为什么用set后面会解释。)里面放所有 s i z e = x size=x size=x 的所有下标, b x b_x bx s i z e size size 属于 x x x 这个块的所有的下标。最后查询时,以 s i z e size size 为第一维,对于散块,在 a a a 中二分,看有没有满足属于 [ l , r ] [l,r] [l,r] 的值,对于整块,则在 b b b 中二分。

接着,我们看向比较难处理的情况三的第二种情况。

我们可以动态维护一个数组,依次储存从根到该节点的链上的所有 s i z e size size 值,可以发现是单调递减。并且记得在加入数组时,删除他在 a , b a,b a,b 中的值,在删除数组元素时,加回去。(因为必须要保证在情况三的情况1解决时要不包含这些在链上的元素)。这里的删除和加入就解释了为什么要采用 set。接着可以先在数组中二分找出满足 n − s i z e y ≤ n 2 n-size_y \le \dfrac{n}{2} nsizey2n y y y 的范围,然后在这个范围中继续二分找出是否存在满足 s i z e y − s i z e z ≤ n 2 size_y-size_z\le \dfrac{n}{2} sizeysizez2n y y y,那么就解决了情况3的第二种情况。

时间复杂度,分块+二分,及 n n log ⁡ n n \sqrt n \log n nn logn,但因为二分的区间长度不可能每次都是顶着的,所以远远达不到这个上界,但仍然很慢。

所以问题就 迎刃而解 了!

代码

#include<bits/stdc++.h>
using namespace std;
int n;
struct node
{int tar,nxt;
}arr[800005];
int fst[400005],cnt;
void adds(int x,int y)
{arr[++cnt].tar=y,arr[cnt].nxt=fst[x],fst[x]=cnt;
}
int size[400005],dfn[400005],tot,rnk[400005];
int klen,len;
set<int> a[400005],b[100005];
bool dp[400005];
void dfs(int x,int last)
{dfn[x]=++tot;rnk[dfn[x]]=x;for(int i=fst[x];i;i=arr[i].nxt){int j=arr[i].tar;if(j==last) continue;dfs(j,x);size[dfn[x]]+=size[dfn[j]];} size[dfn[x]]++;
}
int getk(int x)
{return ceil(x/(klen*1.0));
}
int lk(int x)
{
//	cout<<x<<" "<<(x-1)*klen+1<<endl;return (x-1)*klen+1;
}
int rk(int x)
{if(x==len) return n;else return x*klen;
}
bool ch1(int p,int x,int y)
{
//	cout<<p<<" "<<x<<" "<<y<<endl;set<int>::iterator q=a[p].lower_bound(x);if(q==a[p].end()) return false;else if(*q<=y) return true;else return false;
}
bool ch2(int p,int x,int y)
{set<int>::iterator q=b[p].lower_bound(x);if(q==b[p].end()) return false;else if(*q<=y) return true;else return false;
}
bool check(int l,int r,int x,int y)
{
//	cout<<l<<" "<<r<<" "<<x<<" "<<y<<endl;if(l>r) return false;int p=getk(x)+1,q=getk(y)-1;int pp=rk(getk(x)),qq=lk(getk(y));if(p>q){for(int i=x;i<=y;++i) if(ch1(i,l,r)) return true;}else{for(int i=x;i<=pp;++i) if(ch1(i,l,r)) return true;for(int i=qq;i<=y;++i) if(ch1(i,l,r)) return true;for(int i=p;i<=q;++i) if(ch2(i,l,r)) return true;}return false;
}
int p[400005],tail=400001;
void add(int x)
{p[--tail]=size[dfn[x]];x=dfn[x];set<int>::iterator q=a[size[x]].lower_bound(x);a[size[x]].erase(q);q=b[getk(size[x])].lower_bound(x);b[getk(size[x])].erase(q);
}
void del(int x)
{tail++;x=dfn[x];a[size[x]].insert(x);b[getk(size[x])].insert(x);
}
void get_ans(int x,int last)
{if(x!=1)add(x);int flg=0,num=0,l,r;bool bj=0;for(int i=fst[x];i;i=arr[i].nxt){int j=arr[i].tar;if(j==last) continue;if(size[dfn[j]]>n/2) flg++,num=size[dfn[j]],l=dfn[j],r=dfn[j]+size[dfn[j]]-1;get_ans(j,x);}del(x);if(n-size[dfn[x]]>n/2) flg++,num=n-size[dfn[x]],bj=1;if(flg>=2) return;if(flg==0){dp[x]=1;return;}if(!bj){if(check(l,r,num-n/2,n/2))dp[x]=1;}else{	//x,x+size[x]-1if(check(1,dfn[x]-1,num-n/2,n/2)||check(dfn[x]+size[dfn[x]],n,num-n/2,n/2))dp[x]=1;if(dp[x]){return;}
//		cout<<x<<" "<<1<<endl;int ll=lower_bound(p+tail,p+400000+1,n-n/2)-p,rr=400000,mid,ans;while(ll<=rr){mid=(ll+rr)>>1;if(p[mid]-size[dfn[x]]<=n/2){dp[x]=1;break;}else{rr=mid-1;}}}
}
int main()
{scanf("%d",&n);for(int i=1;i<n;++i){int x,y;scanf("%d%d",&x,&y);adds(x,y);adds(y,x);}klen=sqrt(n),len;if(klen*klen==n) len=klen;else len=klen+1;dfs(1,0);for(int i=1;i<=n;++i) a[size[i]].insert(i),b[getk(size[i])].insert(i);get_ans(1,0);for(int i=1;i<=n;++i) printf("%d ",dp[i]);
}
暴力出奇迹!!!!!

T h e E n d \Huge\mathscr{The\ End} The End

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/13328.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

矩阵系统源码智能回复私信场景开发

抖音矩阵系统源码智能回复私信场景开发 一、要想开发私信功能开发者需要准备的工作 开发者需要先对接官方api接口以及去申请api提交审核&#xff0c;目前需要了解官方对开发者对该能力开发权限的功能符合开发需求&#xff0c;其次需要了解官方私信触达的规则 1.申请流程&…

Codeforces-Round-883-Div-3

Codeforces Round 883 (Div. 3) 链接&#xff1a;https://codeforces.com/contest/1846 A. Rudolph and Cut the Rope There are n n n nails driven into the wall, the i i i-th nail is driven a i a_i ai​ meters above the ground, one end of the b i b_i bi​ m…

Excel 合并单元格筛选时只出现首行

一、问题描述 如果对合并单元格直接筛选&#xff0c;只能筛选出第一个单元格的值 二、原因分析&#xff1a; Excel筛选单元格时&#xff0c;遇到不连续区域&#xff08;即中间有空白单元格&#xff09;会识别不到后续内容&#xff1b; 合并单元格后&#xff0c; 除首行外&…

十八、Jenkins(centos7)执行接口自动化测试脚本,飞书推送测试结果消息

十八、Jenkins&#xff08;centos7&#xff09;执行接口自动化测试脚本&#xff0c;飞书推送测试结果消息 1.创建 Freestyle project 项目 2. 输入git仓库地址 https://gitee.com/HP_mojin/pytest_allure_request_20220811 3. 增加构建步骤-Execute shell&#xff08;Jenkins…

Go 语言数组

Go 语言提供了数组类型的数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列&#xff0c;这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。 相对于去声明 number0, number1, ..., number99 的变量&#xff0c;使用数组形式 numbers[0], num…

Kotlin~Composite组合模式

概念 能够帮助实现树状结构的模式。 主要特点 递归组合树状结构统一处理所有对象 角色介绍 Component: 组合接口Leaf: 叶子节点&#xff0c;无子节点Composite&#xff1a;枝节点&#xff0c;用来存储子部件 UML 代码实现 interface Organ {fun personCount():Int } cla…

springboot dubbo seata nacos集成 分布式事务seata实现

文章目录 Seata介绍dubbo介绍目标版本说明和代码地址pom.xml验证模块microservice-boot-commonmicroservice-boot- plat 验证结果注意事项 Seata介绍 官网&#xff1a;http://seata.io/zh-cn/docs/overview/what-is-seata.html Seata 是一款开源的分布式事务解决方案&#xff…

python实现语音识别(讯飞开放平台)

文章目录 讯飞平台使用python实现讯飞接口的语音识别第一步&#xff1a;导入需要的依赖库第二步&#xff1a;声明全局变量第三步&#xff1a;初始化讯飞接口对象第四步&#xff1a;收到websocket建立连接后的处理函数第五步&#xff1a;收到websocket消息的处理函数第六步&…

scratch 角色追踪

scratch 角色追踪 本程序中一个角色移动到随机位置和方向后向前移动&#xff0c;碰到边缘反弹&#xff1b;另一个角色跟随前一个角色&#xff0c;两个角色接触后前者重新取随机位置。 程序内容如下

Spring Boot 缓存应用实践

缓存是最直接有效提升系统性能的手段之一。个人认为用好用对缓存是优秀程序员的必备基本素质。本文结合实际开发经验&#xff0c;从简单概念原理和代码入手&#xff0c;一步一步搭建一个简单的二级缓存系统。 一、通用缓存接口 1、缓存基础算法 FIFO&#xff08;First In Fir…

生产级Redis Cluster部署(4.0.10版本)

生产级Redis Cluster部署 环境准备 主机名 IP地址 端口 描述 redis-master 192.168.1.51 7000 redis-master01 7001 redis-master02 7002 redis-master03 redis-slave 192.168.1.52 8000 redis-slave01 8001 redis-slave02 8002 redis-slave03 初始化…

Django实现简单的音乐播放器 3

在原有音乐播放器上请求方式优化和增加加载本地音乐功能。 效果&#xff1a; 目录 播放列表优化 设置csrf_token 前端改为post请求 视图端增加post验证 加载歌曲 视图 设置路由 模板 加载layui css 加载layui js 增加功能列表 功能列表脚本实现 最终效果 总结 播…