题目大意

  给定一棵\(n\)个点的树,对于树上每个结点,将它删去,然后可以将得到的森林中任意一个点与其父亲断开并连接到另一颗树上,对每一个点求出森林中所有树\(size\)最大值的最小值。

  \(n\leq 100000\)

题解

  首先用DFS序+可持久化线段树求出删掉这个点后剩下的联通块的大小的最大值\(max\)、次大值\(sec\)、最小值\(min\)。这里要维护两棵可持久化线段树,一棵是DFS序前缀的,一棵是从根到每个点的。

  那么肯定是在最大的连通块上切下一块接到最小的连通块上。

  假设切下的大小为\(x\),那么答案是\(\max(max-x,min+x,sec)\)。这个的图像是带一个向下的尖角的,这个尖角的位置为\(\frac{max+min}{2}\)。所以我们要切下来的\(x\)就是\(\frac{max-min}{2}\)。我们只需要在对应的可持久化线段树上找这个值的前驱和后继并统计答案。

  切下来的部分有三种可能:

   1.在\(x\)的子树内,可以直接统计答案

   2.在\(x\)的子树外且不包含\(x\)到根的点,可以直接统计答案

   3.在\(x\)的子树外切包含根到\(x\)的点,查询到的子树大小要减掉\(size_x\)

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

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<list>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace sgt
{
int rt1[100010];
int rt2[100010];
struct node
{
int lc,rc;
int s;
node()
{
lc=rc=s=0;
}
};
node a[10000010];
int cnt=0;
int insert(int p1,int x,int l,int r)
{
int p=++cnt;
a[p]=a[p1];
a[p].s++;
if(l==r)
return p;
int mid=(l+r)>>1;
if(x<=mid)
a[p].lc=insert(a[p].lc,x,l,mid);
else
a[p].rc=insert(a[p].rc,x,mid+1,r);
return p;
}
int suf(int p1,int p2,int p3,int p4,int x,int l,int r)//p1+p3-p2-p4
{
int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
if(!s)
return 0x3fffffff;
if(l==r)
return l;
int mid=(l+r)>>1;
int ls=a[a[p1].lc].s+a[a[p3].lc].s-a[a[p4].lc].s-a[a[p2].lc].s;
if(x<=mid&&ls)
{
int lans=suf(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,x,l,mid);
if(lans!=0x3fffffff)
return lans;
}
return suf(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,x,mid+1,r);
}
int pre(int p1,int p2,int p3,int p4,int x,int l,int r)
{
int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
if(!s)
return 0;
if(l==r)
return l;
int mid=(l+r)>>1;
int rs=a[a[p1].rc].s+a[a[p3].rc].s-a[a[p4].rc].s-a[a[p2].rc].s;
if(x>mid&&rs)
{
int rans=pre(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,x,mid+1,r);
if(rans)
return rans;
}
return pre(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,x,l,mid);
}
int getmax(int p1,int p2,int p3,int p4,int l,int r)
{
int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
if(!s)
return 0;
if(l==r)
return l;
int mid=(l+r)>>1;
int rs=a[a[p1].rc].s+a[a[p3].rc].s-a[a[p4].rc].s-a[a[p2].rc].s;
if(rs)
return getmax(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,mid+1,r);
return getmax(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,l,mid);
}
int getmin(int p1,int p2,int p3,int p4,int l,int r)
{
int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
if(!s)
return 0x3fffffff;
if(l==r)
return l;
int mid=(l+r)>>1;
int ls=a[a[p1].lc].s+a[a[p3].lc].s-a[a[p4].lc].s-a[a[p2].lc].s;
if(ls)
return getmin(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,l,mid);
return getmin(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,mid+1,r);
}
}
using sgt::rt1;
using sgt::rt2;
using sgt::insert;
using sgt::suf;
using sgt::pre;
using sgt::getmax;
using sgt::getmin;
list<int> l[100010];
int f[100010];
int st[100010];
int ed[100010];
int s[100010];
int w[100010];
int ti;
int n;
void dfs1(int x)
{
st[x]=++ti;
w[ti]=x;
s[x]=1;
for(auto v:l[x])
{
dfs1(v);
s[x]+=s[v];
}
ed[x]=ti;
}
int update(int &a,int &b,int &c)
{
if(c>=a)
{
b=a;
a=c;
return 1;
}
else
{
b=max(b,c);
return 2;
}
return 0;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&n);
int rt,x,y;
int i;
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(x)
{
l[x].push_back(y);
f[y]=x;
}
else
rt=y;
}
dfs1(rt);
for(i=1;i<=n;i++)
{
x=w[i];
rt2[x]=insert(rt2[f[x]],s[x],1,n);
rt1[i]=insert(rt1[i-1],s[x],1,n);
}
for(i=1;i<=n;i++)
{
int mx=0,sec=0,mi=0x7fffffff;
int s1,s2,s3,s4,ans;
int mv; for(auto v:l[i])
{
s1=s[v];
if(update(mx,sec,s1)==1)
{
s4=1;
mv=v;
}
mi=min(mi,s1);
}
s1=n-s[i];
if(s1)
{
if(update(mx,sec,s1)==1)
s4=2;
mi=min(mi,s1);
}
ans=0x7fffffff;
int mid=(mi+mx+1)>>1;
int s5=mx-mid;
if(i==1)
int xxx=1;
ans=min(ans,mx);
if(s4==1)
{
s1=pre(rt1[ed[mv]],rt1[st[mv]-1],0,0,s5,1,n);
s2=suf(rt1[ed[mv]],rt1[st[mv]-1],0,0,s5,1,n);
ans=min(ans,max(sec,max(mi+s1,mx-s1)));
ans=min(ans,max(sec,max(mi+s2,mx-s2)));
}
else if(s4==2)
{
s1=pre(rt2[f[i]],0,0,0,s5+s[i],1,n);
s2=suf(rt2[f[i]],0,0,0,s5+s[i],1,n);
if(s1)
s1-=s[i];
if(s2!=0x3fffffff)
s2-=s[i];
ans=min(ans,max(sec,max(mi+s1,mx-s1)));
ans=min(ans,max(sec,max(mi+s2,mx-s2)));
s1=pre(rt1[st[i]-1],rt1[ed[i]],rt1[n],rt2[f[i]],s5,1,n);
s2=suf(rt1[st[i]-1],rt1[ed[i]],rt1[n],rt2[f[i]],s5,1,n);
ans=min(ans,max(sec,max(mi+s1,mx-s1)));
ans=min(ans,max(sec,max(mi+s2,mx-s2)));
}
printf("%d\n",ans);
}
return 0;
}

【CF768G】The Winds of Winter 可持久化线段树 DFS序的更多相关文章

  1. Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

    题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB    总提交次数:196   AC次数:65   平均分: ...

  2. BZOJ3653谈笑风生——可持久化线段树+dfs序

    题目描述 设T 为一棵有根树,我们做如下的定义: ? 设a和b为T 中的两个不同节点.如果a是b的祖先,那么称“a比b不知道 高明到哪里去了”. ? 设a 和 b 为 T 中的两个不同节点.如果 a ...

  3. BZOJ_3252_攻略_线段树+dfs序

    BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...

  4. 【XSY2534】【BZOJ4817】树点涂色 LCT 倍增 线段树 dfs序

    题目大意 ​ Bob有一棵\(n\)个点的有根树,其中\(1\)号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜 ...

  5. 【bzoj4817】树点涂色 LCT+线段树+dfs序

    Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...

  6. S - Query on a tree HDU - 3804 线段树+dfs序

    S - Query on a tree HDU - 3804   离散化+权值线段树 题目大意:给你一棵树,让你求这棵树上询问的点到根节点直接最大小于等于val的长度. 这个题目和之前写的那个给你一棵 ...

  7. HDU 5692 线段树+dfs序

    Snacks Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  8. 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

    3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] ...

  9. 【BZOJ-3306】树 线段树 + DFS序

    3306: 树 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 792  Solved: 262[Submit][Status][Discuss] De ...

随机推荐

  1. log4j打印堆栈信息

    原文地址:https://blog.csdn.net/xianyu_0418/article/details/6043174 大家都知道,网站在运行的过程中,打印必要的log对记录网站的运行情况.从而 ...

  2. Oja’s rule

    目录 Oja's rule 背景 Hebbian learning 主要的一些理论 论文里面一些主要的假设 引理1 引理2 引理3 定理1 LEMMA 3(ALL) 引理 4 定理 2 定理 3(关于 ...

  3. 亲测可以永久破解2018版本的pycharm

    pycharm是很强大的开发工具,但是每次注册着实让人头疼.网络上很多注册码.注册服务器等等.但都只是一年或者不能用:为次有如下解决方案.亲测有效!!! 如果想让pycharm永久被激活,比如截止日到 ...

  4. anaconda + VSCode + 生产环境配置

    1. 修改jupyter notebook 默认路径: 进入anaconda 命令行, jupyter notebook --generate-config   生成配置文件, 该文件在    本机用 ...

  5. 2 Interrupting Appropriately

    1 Interrupting someone politely e.g. Excuse me for interrupting, but may I ask a question? Sure. Of ...

  6. [转帖]Ipvsadm参数详解(常用命令)

    Ipvsadm参数详解(常用命令) 2013年11月29日 12:41:40 怀素1980 阅读数:15901   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...

  7. Redis 安装学习

    Linux下下载安装redis https://redis.io/download tar -zvxf redisxxx cd redisxxxx make  ---进行安装 vim ~.bash_p ...

  8. Laravel 的十八个最佳实践

    本文翻译改编自 Laravel 的十八个最佳实践 这篇文章并不是什么由 Laravel 改编的 SOLID 原则.模式等. 只是为了让你注意你在现实生活的 Laravel 项目中最常忽略的内容.   ...

  9. synchronized无法禁止指令重排序的证明

    package demo.reorder; import java.util.concurrent.ExecutorService; import java.util.concurrent.Execu ...

  10. mysql关联、子查询索引优化

    1.驱动表:加索引不起作用,因为全表扫描.表1 left join 表2 ,此时表1是驱动表 被驱动表:给这个加索引.  关联查询  子查询时 尽量不使用not in 或者not exists 而是用 ...