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 ...
随机推荐
- js中浏览器兼容startsWith 、endsWith 函数
在做js开发的时候用到了startsWith函数时,发现各个浏览器不兼容问题,因为对开发来说,chrome浏览器最好用,就一直在chrome浏览器中使用这两个函数没有任何问题,但在ie浏览器访问就直接 ...
- TCP连接的11中状态
1.CLOSED:初始状态,表示TCP连接时关闭状态,没有任何连接. 2.LISTEN:表示服务器端的某个SOCKET处于监听状态,可以接收客户端的连接请求. 3.SYN_RCVD:表示服务器接收到来 ...
- EhCache缓存页面、局部页面和对象缓存
页面缓存:SimplePageCachingFilter web.xml <!-- 页面缓存配置,配合ehcache.xml中name为“SimplePageCachingFilter”(默认值 ...
- tf.truncated_normal和tf.random_normal使用方法的区别
1.tf.truncated_normal使用方法 tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=No ...
- redis_ 5 集群
[转自 ]https://www.cnblogs.com/hjwublog/p/5681700.html#_label0 Redis集群简介 Redis 集群是3.0之后才引入的,在3.0之前,使用哨 ...
- LaTeX 加粗
本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/50997822 LaTeX中文本加粗的方 ...
- Cache index coloring for virtual-address dynamic allocators
A method for managing a memory, including obtaining a number of indices and a cache line size of a c ...
- C#中的全局程序集缓存定义
安装有公共语言运行时的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存.全局程序集缓存中存储了专门指定给由计算机中若干应用程序共享的程序集. 应当仅在需要时才将程序集安装到全局程序集缓存中以 ...
- rabbitMQ学习笔记(五) 消息路由
生产者会生产出很多消息 , 但是不同的消费者可能会有不同的需求,只需要接收指定的消息,其他的消息需要被过滤掉. 这时候就可以对消息进行过滤了. 在消费者端设置好需要接收的消息类型. 如果不使用默认的E ...
- DynaActionForm(动态ActionForm)的使用
在struts中利用DynaActionForm(动态ActionForm)可以节省代码的编写. 1.在struts-config.xml中配置DynaActionForm:加入这个Form中有三个属 ...