hihoCoder1381 - Little Y's Tree
Description
给出一个\(n(n\leq10^5)\)个点的带边权的树。进行\(Q\)次询问:每次删除树上的\(k\)条边,求剩下的\(k+1\)个连通块中最远点对距离的和。\(\Sigma k\leq10^5\),询问之间是独立的。
Solution
神奇而又毒瘤的做法。
考虑如何合并树上两个连通块的答案。设两个连通块的最远点对分别为\((v_1,v_2),(v_3,v_4)\),那么合并后的最远点对的两个端点一定是\(\{v_1,v_2,v_3,v_4\}\)中的两个。LCA用RMQ求的话时间复杂度是\(O(1)\)。
证明:
设两个连通块通过边\((p,q)\)连通。若合并后的最远路径不经过\((p,q)\),则其一定是\((v_1,v_2),(v_3,v_4)\)之一。若经过\((p,q)\),则可以将其看成\((u_1,p)+(p,q)+(q,u_2)\),而\((v_1,p),(v_2,p)\)必然是以\(p\)为端点的最长、次长路径,所以\(u_1\)必然是\(v_1,v_2\)之一;\(u_2\)同理。
删除边\((u,v)\)相当于将以\(v\)为根的子树从原树上断掉,断掉\(k\)条边相当于将原树变成了以\(1\)和\(v_{1..k}\)为根的\(k+1\)个连通块。那么做出原树的DFS序,断掉一个子树就相当于删掉一个区间。如图,子树\(1\)中的子树\(2\)和子树\(6\)被断掉,那么就删掉这两个区间,剩下的\(\{1,3\}\)即以\(1\)为根的连通块;同理\(2\)中的\(4\)被断掉,从\(2\)的DFS序中删掉\(4\)的就是\(\{2,5\}\)。

对所有区间排序并递归,可以求出每个连通块中有哪些点,那么该连通块中的最远点对相当于DFS序上的若干个区间的合并。由于新加入一个区间最多把原区间分成三份,所以最多要询问\(2k+1\)次。用线段树维护DFS序,每个节点记录该区间内的最远点对即可。虽然DFS上连续的点在原树上不一定连通,不过由于我们每次询问的部分都是连通的所以没关系啦。
时间复杂度\(O(logn\Sigma k)\)。
Code
//Little Y's Tree
#include <algorithm>
#include <cstdio>
using std::sort; using std::max; using std::swap;
typedef long long lint;
inline char gc()
{
static char now[1<<16],*s,*t;
if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
return *s++;
}
inline int read()
{
int x=0; char ch=gc();
while(ch<'0'||'9'<ch) ch=gc();
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x;
}
const int N=1e5+10;
int n;
int cnt,h[N];
struct edge{int u,v,w,nxt;} ed[N<<1];
void edAdd(int u,int v,int w)
{
cnt++; ed[cnt].u=u,ed[cnt].v=v,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt;
cnt++; ed[cnt].u=v,ed[cnt].v=u,ed[cnt].w=w,ed[cnt].nxt=h[v],h[v]=cnt;
}
int fa[N],dpt[N]; lint dst[N];
int dfCnt1,dfn1[N],fr1[N],to1[N];
int dfCnt2,dfn2[N<<1],fr2[N];
void dfs(int u)
{
dfn1[++dfCnt1]=u; fr1[u]=dfCnt1;
dfn2[++dfCnt2]=u; fr2[u]=dfCnt2;
for(int i=h[u];i;i=ed[i].nxt)
{
int v=ed[i].v,w=ed[i].w;
if(v==fa[u]) continue;
fa[v]=u,dpt[v]=dpt[u]+1,dst[v]=dst[u]+w;
dfs(v); dfn2[++dfCnt2]=u;
}
to1[u]=dfCnt1;
}
int Lg2[N<<1],rmq[N<<1][20];
void bldLCA()
{
Lg2[1]=0;
for(int i=2;i<=dfCnt2;i++) Lg2[i]=Lg2[i>>1]+1;
for(int i=1;i<=dfCnt2;i++) rmq[i][0]=dfn2[i];
for(int k=1;k<=18;k++)
for(int i=1;i+(1<<k-1)<=dfCnt2;i++)
{
int r1=rmq[i][k-1],r2=rmq[i+(1<<k-1)][k-1];
rmq[i][k]=dpt[r1]<dpt[r2]?r1:r2;
}
}
int lca(int u,int v)
{
int i=fr2[u],j=fr2[v];
if(i>j) swap(i,j);
int t=Lg2[j-i+1];
int r1=rmq[i][t],r2=rmq[j-(1<<t)+1][t];
return dpt[r1]<dpt[r2]?r1:r2;
}
lint dist(int u,int v) {return dst[u]+dst[v]-2*dst[lca(u,v)];}
#define Ls (p<<1)
#define Rs (p<<1|1)
int rt=1; int maxP=0;
struct node
{
lint len; int v1,v2;
node(lint _len=0,int _v1=0,int _v2=0) {len=_len,v1=_v1,v2=_v2;}
}nd[N<<2];
node operator +(node x,node y)
{
if(x.v1==0) return y; else if(y.v1==0) return x;
node z=node(0,0,0);
lint d[10],d0=0;
d[1]=dist(x.v1,x.v2),d[2]=dist(x.v1,y.v1),d[3]=dist(x.v1,y.v2);
d[4]=dist(x.v2,y.v1),d[5]=dist(x.v2,y.v2),d[6]=dist(y.v1,y.v2);
for(int i=1;i<=6;i++) d0=max(d0,d[i]);
if(d[1]==d0) z=node(d[1],x.v1,x.v2);
else if(d[2]==d0) z=node(d[2],x.v1,y.v1);
else if(d[3]==d0) z=node(d[3],x.v1,y.v2);
else if(d[4]==d0) z=node(d[4],x.v2,y.v1);
else if(d[5]==d0) z=node(d[5],x.v2,y.v2);
else if(d[6]==d0) z=node(d[6],y.v1,y.v2);
return z;
}
void update(int p) {nd[p]=nd[Ls]+nd[Rs];}
void bldTr(int p,int L0,int R0)
{
maxP=max(maxP,p);
if(L0==R0) {nd[p]=node(0,dfn1[L0],dfn1[L0]); return;}
int mid=L0+R0>>1;
bldTr(Ls,L0,mid),bldTr(Rs,mid+1,R0);
update(p);
}
int optL,optR;
node query(int p,int L0,int R0)
{
if(optL<=L0&&R0<=optR) return nd[p];
int mid=L0+R0>>1; node res=node(0,0,0);
if(optL<=mid) res=res+query(Ls,L0,mid);
if(mid<optR) res=res+query(Rs,mid+1,R0);
return res;
}
struct qRec{int fr,to; node ans;} q[N];
bool cmpQ(qRec x,qRec y) {return x.fr<y.fr;}
int m,now;
//solve(x)解决区间x及其内部区间,并将now移动到x外的第一个
void solve(int x)
{
if(x>m) return;
now++; int pre=q[x].fr;
while(now<=m&&q[now].to<=q[x].to)
{
optL=pre,optR=q[now].fr-1;
if(optL<=optR) q[x].ans=q[x].ans+query(rt,1,n);
pre=q[now].to+1;
solve(now);
}
optL=pre,optR=q[x].to;
if(optL<=optR) q[x].ans=q[x].ans+query(rt,1,n);
}
int main()
{
n=read();
for(int i=1;i<=n-1;i++)
{
int u=read(),v=read(),w=read();
edAdd(u,v,w);
}
fa[1]=0,dfs(1);
bldLCA(); bldTr(rt,1,n);
int Q=read();
while(Q--)
{
m=read();
for(int i=1;i<=m;i++)
{
int x=read()<<1; int u=ed[x].u,v=ed[x].v;
if(dpt[u]>dpt[v]) swap(u,v);
q[i].fr=fr1[v],q[i].to=to1[v];
q[i].ans=node(0,0,0);
}
m++; q[m].fr=1,q[m].to=n,q[m].ans=node(0,0,0);
sort(q+1,q+m+1,cmpQ);
solve(now=1);
lint res=0;
for(int i=1;i<=m;i++) res+=(q[i].ans).len;
printf("%lld\n",res);
}
return 0;
}
P.S.
Icefox不到100行orz,我写了150+
hihoCoder1381 - Little Y's Tree的更多相关文章
- DFS序+线段树 hihoCoder 1381 Little Y's Tree(树的连通块的直径和)
题目链接 #1381 : Little Y's Tree 时间限制:24000ms 单点时限:4000ms 内存限制:512MB 描述 小Y有一棵n个节点的树,每条边都有正的边权. 小J有q个询问,每 ...
- [hihoCoder#1381]Little Y's Tree
[hihoCoder#1381]Little Y's Tree 试题描述 小Y有一棵n个节点的树,每条边都有正的边权. 小J有q个询问,每次小J会删掉这个树中的k条边,这棵树被分成k+1个连通块.小J ...
- Size Balance Tree(SBT模板整理)
/* * tree[x].left 表示以 x 为节点的左儿子 * tree[x].right 表示以 x 为节点的右儿子 * tree[x].size 表示以 x 为根的节点的个数(大小) */ s ...
- HDU3333 Turing Tree(线段树)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=3333 Description After inventing Turing Tree, 3x ...
- Codeforces 620E New Year Tree(DFS序 + 线段树)
题目大概说给一棵树,树上结点都有颜色(1到60),进行下面两个操作:把某结点为根的子树染成某一颜色.询问某结点为根的子树有多少种颜色. 子树,显然DFS序,把子树结点映射到连续的区间.而注意到颜色60 ...
- Linux/Ubuntu tree 命令以树形结构显示文件夹目录结构
1.安装命令工具 sudo apt-get -y install tree 2.可以查看关于tree命令的帮助信息 $ tree --help usage: tree [-adfghilnpqrstu ...
- POJ3321 Apple Tree(DFS序)
题目,是对一颗树,单点修改.子树查询.典型的dfs序入门题. DFS序可以将一颗树与子树们表示为一个连续的区间,然后用线段树来维护:感觉算是树链剖分的一种吧,和轻重链剖分不同的是这是对子树进行剖分的. ...
- Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 2588: Spoj 10628. Count on a tree Time Limit ...
- poj3237 Tree
Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...
随机推荐
- css水平垂直居中的几个方法和技巧/居中之美
水平居中设置-行内元素 我们在实际工作中常会遇到需要设置水平居中场景,今天我们就来看看怎么设置水平居中的. 如果被设置元素为文本.图片等行内元素时,水平居中是通过给父元素设置 text-ali ...
- JavaScript数据格式验证探讨
1.需求 修改某个文本框数据,要求对修改后的格式做验证(必须是数字). 注:实际需求比上述复杂,为了说明问题,这里特意简化了需求(如:对修改后数据依赖条件的判断,数据入库等). 2.关于NaN的探讨( ...
- python 基础网络编程1
python 基础网络编程1 Source code: Lib/socketserver.py lib的主目录下有一个sockserver.py文件, 里面是python基本的网络编程模型 共有一个b ...
- Codeforces Round #316 (Div. 2) D Tree Requests
官方题解是离线询问,dfs树形转线性,然后二分找区间. 还有一种比较好的做法是直接dfs,将当前访问这个结点u相关的询问之前的状态存起来,然后访问完以后利用异或开关性,得到这颗子树上的答案. 代码是学 ...
- 2019的hdu暑假作业(欢迎纠错)
1219 遍历计数. #include<bits/stdc++.h> #define QAQ 0 using namespace std; ]; ]; int main(){ )){ me ...
- Luogu P3627 抢掠计划
题目传送门 \(Tarjan\)缩点+SPFA最长路 #include<iostream> #include<cstdio> #include<cstring> # ...
- shell脚本,提示用户输入一个用户名,如果存在;显示用户UID和SHELL信息;否则,则显示无此用户;显示完成之后,提示用户再次输入;如果是quit则退出;
[root@localhost wyb]# cat tishiuser.sh #!/bin/bash #提示用户输入一个用户名,如果存在:显示用户UID和SHELL信息:否则, #则显示无此用户:显示 ...
- 什么是无符号段整数,什么又是有符号数,(c++与java语言里边的不同)
c++中:整型数分为有符号数和无符号数两种 unsigned int a;无符号整型变量a,意思是这个数最小值为0,最大值为2的32次方-1,因为一个整型数占四个字节,一个字节8位,共32位 int ...
- x220 OS X 10.10.4安装
变色龙安装过程: 1.使用磁盘助手将按照盘写入独立的磁盘分区(AF格式,就是Apple的HPS格式): 2.安装启动时,用-v -f -x参数,分别为显示信息.重新build系统驱动.安全模式: 3. ...
- 【Java_基础】cmd下使用java命令运行class文件提示“错误:找不到或无法加载主类“的问题分析
1.问题如下 当在命令行使用java命令执行字节码文件时提示“错误:找不到或无法加载主类” 2. 问题分析 这是由于在运行时类的全名应该是包名+类名,例如在包net.xsoftlab.baike下的类 ...