给出一颗$N$个节点的树,现在我们**在原图中**每个不直接连边但是中间只间隔一个点的两个点之间连一条边. 比如**在原图中**$u$与$v$连边,$v$与$w$连边,但是$u$与$w$不连边,这时候我们就需要连一条$u$与$v$的边. 现在我们需要求出新图中每一个点对$(i,j)\ (1 \leq i \leq j \leq N)$的经过的边数和.

因为实在太菜了不会树形dp的只好用点分了……

(点分是个好东西所有树的题目都可以暴力艹过去)

首先,设原点对之间距离为$dis$,如果$dis$是奇数,那么新的距离为$(dis>>1)+1$,否则为$dis>>1$

我们考虑如何计算经过当前根节点的路径对答案的贡献

我们设已经dfs过的子树中有$cnt0$条长度为偶数的链,$dis0$这些链的每一条的长度除以二之和,同理$cnt1$和$dis1$表示长度为奇数的链的条数和每一条链的长度除以二加一的和

简单来讲$dis1$和$dis0$分别表示将原$dis$按上面的方法转化后的答案

然后考虑当前子树$v$,设$v$中的以上信息分别为,$cnt[0],cnt[1],dis[0],dis[1]$

然后就是路径的两两组合分别计入答案就是了

顺便注意因为两条奇数路径合起来变为偶数,所以相当于每一条路径的长度都要减一

然后别忘了加上子树单独的贡献

 //minamoto
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
const int N=2e5+;
int head[N],Next[N<<],ver[N<<],tot;
int cnt[];ll dis[],ans=;
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
}
int sz[N],son[N],size,vis[N],rt,n;
void findrt(int u,int fa){
sz[u]=,son[u]=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(vis[v]||v==fa) continue;
findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
}
cmax(son[u],size-sz[u]);
if(son[u]<son[rt]) rt=u;
}
void dfs(int u,int fa,int d){
d&?(++cnt[],dis[]+=((d>>)+)):(++cnt[],dis[]+=(d>>));
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa&&!vis[v]) dfs(v,u,d+);
}
}
void getans(int u){
int cnt0=,cnt1=;ll dis0=,dis1=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(vis[v]) continue;
cnt[]=cnt[]=,dis[]=dis[]=;
dfs(v,u,);
// ans+=dis[1]*dis1-cnt[1]*cnt1;
// ans+=dis[1]*dis0;
// ans+=dis[0]*dis1;
// ans+=dis[0]*dis0;
ans+=dis[]*cnt1+dis1*cnt[]-1ll*cnt1*cnt[];
ans+=dis[]*cnt0+dis0*cnt[];
ans+=dis[]*cnt1+dis1*cnt[];
ans+=dis[]*cnt0+dis0*cnt[];
ans+=dis[]+dis[];
cnt0+=cnt[],cnt1+=cnt[];
dis0+=dis[],dis1+=dis[];
}
}
void solve(int u){
vis[u]=,getans(u);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(vis[v]) continue;
size=sz[v],rt=,findrt(v,u);
solve(rt);
}
}
int main(){
// freopen("testdata.in","r",stdin);
n=read();
for(int i=,u,v;i<n;++i)
u=read(),v=read(),add(u,v),add(v,u);
son[rt=]=n+,size=n,findrt(,);
solve(rt);
printf("%I64d\n",ans);
return ;
}

CF1060E Sergey and Subway(点分治)的更多相关文章

  1. [CF1060E]Sergey and Subway[树dp]

    题意 给出 \(n\) 个点的树,求 \(\sum_{i=1}^n{\sum_{j=i}^n{\lceil \frac{dis(i,j)}{2} \rceil}}\) . \(n\leq 2 \tim ...

  2. CF1060E Sergey and Subway 思维

    分两种情况讨论 一种为奇数长为$L$的路径,在经过变化后,我们需要走$\frac{L}{2} + 1$步 一种为偶数长为$L$的路径,在变化后,我们需要走$\frac{L}{2}$步 那么,我们只需要 ...

  3. cf1060E. Sergey and Subway(树形dp)

    题意 题目链接 Sol 很套路的题 直接考虑每个边的贡献,最后再把奇数点的贡献算上 #include<bits/stdc++.h> #define Pair pair<int, in ...

  4. CF1060E Sergey and Subways 假的点分治

    题目传送门:http://codeforces.com/problemset/problem/1060/D 题意:给出$N$个点的一棵树,现在将距离为$2$的点之间连一条边,求所有点对之间最短路的和, ...

  5. 1060E Sergey and Subway(思维题,dfs)

    题意:给出一颗树,现在,给哪些距离为2的点对,加上一条边,问所有点对的距离和 题解:如果没有加入新的边,距离和就会等于每条边的贡献,由于是树,我们用点来代表点上面的边,对于每条边,它的贡献将是(子树大 ...

  6. CF 1060E. Sergey and Subway

    题目链接 题意:给你一棵树,然后连接两个有公共邻居的点,问你连完后,任意两点的距离之和. 一开始看这种题,还不怎么会做,借鉴了这位大佬的博客,get到了新技能,当我们求树上任意俩点的距离之时,可以转化 ...

  7. E. Sergey and Subway

    比赛时候写复杂了…… 我写的是 计算每个节点树内所有点到某个点的距离和. #include <bits/stdc++.h> using namespace std; typedef lon ...

  8. 【非原创】codeforces 1060E Sergey and Subway 【树上任意两点距离和】

    学习博客:戳这里 本人代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 con ...

  9. Codeforces Round #513 游记

    Codeforces Round #513 游记 A - Phone Numbers 题目大意: 电话号码是8开头的\(1\)位数字.告诉你\(n(n\le100)\)个数字,每个数字至多使用一次.问 ...

随机推荐

  1. Json——使用Json jar包实现Json字符串与Java对象或集合之间的互相转换

    总结一下利用Json相关jar包实现Java对象和集合与Json字符串之间的互相转换: 1.创建的User类: package com.ghj.packageofdomain; public clas ...

  2. HTML制作练习

  3. boost::serialization 用基类指针转存派生类(错误多多,一波三折)

    boost::serialization 也支持c++的多态,这样我们就能够通过使用基类的指针来转存派生类, 我们接着上一篇( boost::serialization(2)序列化基类 )的样例来看: ...

  4. Posix消息队列相关函数

    Posix消息队列(message queue) IPC函数中常用的参数取值: 打开或创建POSIX IPC对象所用的各种oflag常值o_RDONLY   只读O_WRONLY   只写O_RDWD ...

  5. Creating a .bash_profile on your mac

    A typical install of OS X won't create a .bash_profile for you. When you want to run functions from ...

  6. Android Camera系统深入理解

    1. Android Camera系统架构 http://blog.csdn.net/myarrow/article/details/8489674

  7. vue 单页面(SPA) history模式调用微信jssdk 跳转后偶尔 "invalid signature"错误解决方案

    项目背景 vue-cli生成的单页面项目,router使用history模式.产品会在公众号内使用,需要添加微信JSSDK,做分享相关配置. 遇到的问题 相关配置与JS接口安全域名都已经ok,发布后, ...

  8. 常用到的JS 验证(包括例子)

    //验证是否为空    function check_blank(obj, obj_name){         if(obj.value != ''){                  retur ...

  9. install build tools 25.0.2 and sync the project

    install build tools 25.0.2 and sync the project in android studio bundle.gradle,将buildToolsVersion修改 ...

  10. js 中整理(一)

    一. 冒泡与冒泡阻止   var arr={5,0,-56,900,12,9000,-123}; var flag=false; //大的排序次数(arr.length-1) for(var i=0; ...