51nod 1576 Tree and permutation(树的重心+dfn序)
乍一看我不会。
先不考虑加点。
先考虑没有那个除\(2\)。
考虑每一条边的贡献,假设某一条边把这棵树分成大小为x,y的两个部分。
那么这条边最多可以被使用\(min(x,y)*2\)次(因为有进有出),即贡献最大为\(min(x,y)*2*\)这条边的权值。
那么能不能让每一条边的被使用达到最大呢?
显然可以。
那怎么快速算这个东西呢?不可能每加一个点就dfs一遍吧。那样就\(T\)飞了。
实际上这个东西就是每个点到树的重心的距离\(*2\)。
为什么?因为满足以树的重心为根每一个子树大小\(<\)总共的节点数。每一棵子树内所有点都要向子树外也就是根(重心)连边。
然后发现这个除\(2\)没有向下取整。
所以就是求所有的点到重心的距离和。
然后加点的话可以离线然后\(dfn序\)维护一下。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define ls now<<1
#define rs now<<1|1
const int N=101000;
int cnt,head[N];
struct edge{
int to,nxt;
}e[N*2];
void add_edge(int u,int v){
cnt++;
e[cnt].nxt=head[u];
e[cnt].to=v;
head[u]=cnt;
}
int size[N],fa[N][22],dep[N],dfn[N],tot;
void dfs(int u,int f){
size[u]=1;
dfn[u]=++tot;
fa[u][0]=f;dep[u]=dep[f]+1;
for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
int maxson=-1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f)continue;
dfs(v,u);
size[u]+=size[v];
}
}
int getlca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int sum[N*4];
void update(int now){
sum[now]=sum[ls]+sum[rs];
}
void build(int l,int r,int now){
if(l==r){
if(l==1)sum[l]=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
update(now);
}
void add(int l,int r,int x,int now){
if(l==r){
sum[now]=1;
return;
}
int mid=(l+r)>>1;
if(x>mid)add(mid+1,r,x,rs);
else add(l,mid,x,ls);
update(now);
}
int check(int l,int r,int L,int R,int now){
if(l==L&&r==R)return sum[now];
int mid=(l+r)>>1;
if(L>mid)return check(mid+1,r,L,R,rs);
else if(R<=mid)return check(l,mid,L,R,ls);
else return check(l,mid,L,mid,ls)+check(mid+1,r,mid+1,R,rs);
}
int up(int x,int y){
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>dep[y])x=fa[x][i];
return x;
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
int n,root;
long long ans;
int main(){
n=read();n++;
root=1;ans=0;
for(int i=2;i<=n;i++){
int v=read();
add_edge(i,v);add_edge(v,i);
}
dfs(1,0);
build(1,n,1);
for(int i=2;i<=n;i++){
add(1,n,dfn[i],1);
int lca=getlca(root,i);
ans+=(long long)(dep[root]+dep[i]-2ll*dep[lca]);
if(lca==root){
int x=up(i,root);
int sizex=check(1,n,dfn[x],dfn[x]+size[x]-1,1);
if(sizex>i-sizex)root=x,ans+=(long long)(i-sizex*2ll);
}
else{
int x=fa[root][0];
int sizex=check(1,n,dfn[root],dfn[root]+size[root]-1,1);
if(i-sizex>sizex)root=x,ans+=(long long)(sizex*2ll-i);
}
printf("%lld\n",ans);
}
return 0;
}
51nod 1576 Tree and permutation(树的重心+dfn序)的更多相关文章
- POJ 2378 Tree Cutting (树的重心,微变形)
题意: 给定一棵树,n个节点,若删除点v使得剩下的连通快最大都不超过n/2,则称这样的点满足要求.求所有这样的点,若没有这样的点,输出NONE. 思路: 只需要拿“求树的重心”的代码改一行就OK了.因 ...
- HDU6446 Tree and Permutation(树、推公式)
题意: 给一棵N个点的树,对应于一个长为N的全排列,对于排列的每个相邻数字a和b,他们的贡献是对应树上顶点a和b的路径长,求所有排列的贡献和 思路: 对每一条边,边左边有x个点,右边有y个点,x+y= ...
- CF383C Propagating tree (线段树,欧拉序)
\(tag\)没开够\(WA\)了一发... 求出\(dfs\)序,然后按深度分类更新与查询. #include <iostream> #include <cstdio> #i ...
- 洛谷P3703 [SDOI2017]树点涂色(LCT,dfn序,线段树,倍增LCA)
洛谷题目传送门 闲话 这是所有LCT题目中的一个异类. 之所以认为是LCT题目,是因为本题思路的瓶颈就在于如何去维护同颜色的点的集合. 只不过做着做着,感觉后来的思路(dfn序,线段树,LCA)似乎要 ...
- POJ 2378.Tree Cutting 树形dp 树的重心
Tree Cutting Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 4834 Accepted: 2958 Desc ...
- 51Nod 1737 配对(树的重心)
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1737 题意: 思路: 树的重心. 树的重心就是其所以子树的最大的子树结点 ...
- Codeforces Round #268 (Div. 1) 468D Tree(杜教题+树的重心+线段树+set)
题目大意 给出一棵树,边上有权值,要求给出一个1到n的排列p,使得sigma d(i, pi)最大,且p的字典序尽量小. d(u, v)为树上两点u和v的距离 题解:一开始没看出来p需要每个数都不同, ...
- 点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)
推荐YCB的总结 推荐你谷ysn等巨佬的详细题解 大致流程-- dfs求出当前树的重心 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成) 容斥减去不合法情况(两条子路径 ...
- POJ3107Godfather[树形DP 树的重心]
Godfather Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6121 Accepted: 2164 Descrip ...
随机推荐
- LVS的使用
lvs: Linux Virtual Server l4:四层交换:四层路由: 根据请求报文的目标IP和PORT将其转发至后端主机集群中的某一台主机(根据挑选算法): netfilter: PRERO ...
- PHP学习总结(10)——PHP入门篇之自定义网站根目录
- wcf的Contract中name的使用
name可以自定义,Contract中的name会更改soap消息中的名称,虽然不影响在服务端代码中的使用 可以看到,在后台代码中使用函数的重用进行编写代码是非常方便的
- 工具-VS常用快捷键
项目管理: Ctrl+Shift+N: 新建项目 Ctrl+Shift+O: 打开项目 Ctrl+Shift+S: 全部保存 Shift+Alt+C: 新建类 Ctrl+Shift+A: 新建项 Sh ...
- [SharePoint2010开发入门经典]编译部署SPS WebPart
本章概要: 1.理解web部件,什么时候需要创建一个 2.理解标准和可视web部件的不同 3.使用VS构建部署web部件
- Scrapy系列教程(6)------怎样避免被禁
避免被禁止(ban) 有些网站实现了特定的机制,以一定规则来避免被爬虫爬取. 与这些规则打交道并不easy,须要技巧,有时候也须要些特别的基础. 假设有疑问请考虑联系 商业支持 . 以下是些处理这些网 ...
- 一起talk C栗子吧(第一百一十九回:C语言实例--线程死锁三)
各位看官们.大家好,上一回中咱们说的是线程死锁的样例,这一回咱们继续说该样例.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,由于篇幅的原因我们在上一回仅仅介绍了死锁发生的第一种原因,今天 ...
- Codeforces Round #260 (Div. 1)——Civilization
题目链接 题意: n个点,m条边的森林.q次操作. 每次操作:1.询问x所在树的直径 2.合并x和y所在的树,使得合并后的直径最小 ) 分析: 没有读到图是森林.. .做的好纠结 最開始将每一个树都求 ...
- 使用rsync同步数据(by quqi99)
作者:张华 发表于:2015-12-28版权声明:能够随意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 ( http://blog.csdn.net/quqi99 ) 急需使 ...
- hadoop 计数器
一.hadoop有非常多自带的计数器,相信看过执行log的都会看到各种数据 二.用户自己定义计数器 在开发中常常须要记录错误的数据条数,就能够用计数器来解决. 1.定义:用一个枚举来定义一组计数器,枚 ...