题目

题目大意

给你一棵树,然后有一堆询问,每次给出两个点。

问所有点到两个点中最近点的距离的最大值。


正解

本来打了倍增,然后爆了,也懒得调……

显然可以在两个点之间的路径的中点处割开,一边归一个点管。

有个比较显然的思路是DP,设\(f_x\)表示\(x\)子树内的最远点,\(g_x\)向父亲那边走的最远点。

然后就可以倍增搞,合并一下……

代码复杂度极高。

然后有个简单又自然的思路是直接打\(LCT\)。直接把中间那条边断掉,然后求最远点即可。

想法倒是简单自然,接下来的问题就是,如何求最远点?

每条链可以维护子树到链顶的距离。设最远距离为\(len\),则合并的时候,就是左区间的\(len\)、右区间的\(len\)加上左区间的\(siz\)加\(1\)、中间的答案(包括虚边转移过来的)加左区间的\(siz\)。这三个东西取最大值。

至于虚边信息,由于维护的是最大值,不能像和一样加加减减,所以要用multiset维护(或者手写数据结构也可以哈)。

然后你就会发现一个bug——翻转的时候怎么办?

\(len\)的计算是不满足交换律的,所以我们要维护一个\(len2\),表示\(len\)反过来的样子。合并的时候跟上面相反就是了。换句话来说,就是在重链管的整棵子树中,到达链底的最远距离。

翻转的时候直接将两个交换就行了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 100010
#define INF 1000000000
int n,m;
struct Node{
Node *fa,*c[2];
bool is_root,rev;
int siz,len1,len2,mx;
multiset<int> s;
inline void reverse(){
swap(c[0],c[1]);
swap(len1,len2);
rev^=1;
}
inline void pushdown(){
if (rev){
c[0]->reverse();
c[1]->reverse();
rev=0;
}
}
void push(){
if (!is_root)
fa->push();
pushdown();
}
inline void update(){
siz=c[0]->siz+c[1]->siz+1;
len1=max(max(c[0]->len1,c[0]->siz+1+c[1]->len1),c[0]->siz+mx+1);
len2=max(max(c[1]->len2,c[1]->siz+1+c[0]->len2),c[1]->siz+mx+1);
}
inline bool getson(){return fa->c[0]!=this;}
inline void rotate(){
Node *y=fa,*z=y->fa;
if (y->is_root){
is_root=1;
y->is_root=0;
}
else
z->c[y->getson()]=this;
int k=getson();
fa=z;
y->c[k]=c[k^1],c[k^1]->fa=y;
c[k^1]=y,y->fa=this;
siz=y->siz,len1=y->len1,len2=y->len2;
y->update();
}
inline void splay(){
push();
while (!is_root){
if (!fa->is_root){
if (getson()!=fa->getson())
rotate();
else
fa->rotate();
}
rotate();
}
}
} d[N],*null=d;
inline Node *access(Node *x){
Node *y=null;
for (;x!=null;y=x,x=x->fa){
x->splay();
if (x->c[1]!=null){
x->s.insert(x->c[1]->len1);
x->mx=max(x->mx,x->c[1]->len1);
}
x->c[1]->is_root=1;
x->c[1]=y;
y->is_root=0;
if (x->fa!=null){
x->fa->s.erase(x->fa->s.find(x->len1));
if (x->len1==x->fa->mx)
x->fa->mx=(x->fa->s.empty()?-INF:*x->fa->s.rbegin());
}
x->update();
}
return y;
}
inline Node *mroot(Node *x){
Node *t=access(x);
t->reverse();
return t;
}
inline void link(Node *u,Node *v){
mroot(u),u->splay();
Node *t=mroot(v);
t->fa=u;
u->s.insert(t->len1);
if (t->len1>u->mx){
u->mx=t->len1;
u->update();
}
}
Node *find(Node *t,int k){
t->pushdown();
if (k<=t->c[0]->siz)
return find(t->c[0],k);
if (k>t->c[0]->siz+1)
return find(t->c[1],k-t->c[0]->siz-1);
return t;
}
int main(){
scanf("%d",&n);
*null={null,null,null,0,0,0,-1,-1,-INF};
for (int i=1;i<=n;++i)
d[i]={null,null,null,1,0,1,0,0,-INF};
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(&d[u],&d[v]);
}
scanf("%d",&m);
for (int i=1;i<=m;++i){
int u,v,l,ans=0;
scanf("%d%d",&u,&v);
mroot(&d[u]);
Node *a=access(&d[v]),*b;
a=find(a,a->siz>>1),a->splay();
for (b=a->c[1];b->c[0]!=null;b=b->c[0])
b->pushdown();
b->splay();
b->c[0]=null,b->update();
ans=max(a->len1,b->len2);
printf("%d\n",ans);
b->c[0]=a,b->update();
}
return 0;
}

总结

如果要让\(LCT\)支持换根操作,并且维护的不满足交换律的东西的时候,要维护它的反向信息。

[JZOJ3690] 【CF418D】Big Problems for Organizers的更多相关文章

  1. 【leetcode】com/problems/surrounded-regions/

    dfs 栈溢出,bfs超时,用dfs非递归就不溢出了,前后写了1一个星期class node { int i; int j; public node(int i1,int j1) { i=i1; j= ...

  2. 【转】Rendering Problems The following classes could not be instantiated

    xml 设计时警告 打开es/values/目录下styles.xml文件. 把:<style name="AppTheme" parent="Theme.AppC ...

  3. 【AtCoder】AGC005 F - Many Easy Problems 排列组合+NTT

    [题目]F - Many Easy Problems [题意]给定n个点的树,定义S为大小为k的点集,则f(S)为最小的包含点集S的连通块大小,求k=1~n时的所有点集f(S)的和取模92484403 ...

  4. 【CodeForces】913 D. Too Easy Problems

    [题目]D. Too Easy Problems [题意]给定n个问题和总时限T,每个问题给定时间ti和限制ai,当解决的问题数k<=ai时问题有效,求在时限T内选择一些问题解决的最大有效问题数 ...

  5. [原]Water Water Union-Find Set &amp; Min-Spanning Tree Problems&#39; Set~Orz【updating...】

    [HDU] 1213 - How Many Tables [基础并查集,求父节点个数] 1856 -More is better [基础并查集,注意内存,HDU数据水了,不用离散化,注意路径压缩的方式 ...

  6. [原]Water Water Search Problems&#39; Set~Orz【updating...】

    [HDU] [POJ] 作者:u011652573 发表于2014-4-30 10:39:04 原文链接 阅读:30 评论:0 查看评论

  7. POJ 2151 Check the difficulty of problems:概率dp【至少】

    题目链接:http://poj.org/problem?id=2151 题意: 一次ACM比赛,有t支队伍,比赛共m道题. 第i支队伍做出第j道题的概率为p[i][j]. 问你所有队伍都至少做出一道, ...

  8. POJ No.3617【B008】

    [B007]Best Cow Line[难度B]———————————————————————————————————————————————— [Description    支持原版从我做起!!! ...

  9. 【RobotFramework】Selenium2Library类库关键字使用说明

    Add CookieArguments:[ name | value | path=None | domain=None | secure=None | expiry=None ]Adds a coo ...

随机推荐

  1. 骑马修栅栏 Riding the Fences

    题目背景 Farmer John每年有很多栅栏要修理.他总是骑着马穿过每一个栅栏并修复它破损的地方. 题目描述 John是一个与其他农民一样懒的人.他讨厌骑马,因此从来不两次经过一个栅栏.你必须编一个 ...

  2. 案例-开门效果CSS3

    <style> .door { width: 288px; height: 153px; border: 2px solid #333; margin: 150px auto; backg ...

  3. 【模板篇】Link Cut Tree模板(指针)

    网上一片一片的LCT都是数组写的 orz 用指针写splay的人想用指针写LCT找板子都不好找QAQ 所以能A题了之后自然要来回报社会, 把自己的板子丢上来(然而根本没有人会看) LCT讲解就省省吧, ...

  4. C# 记录循环消耗时间

    今天写了循环段代码,但是感觉好像性能很差的样子,就想看一下整个循环的执行时间,最开始我想到了DateTime.Now,但是诡异的是,如果我循环的次数比较少的话(少于30000次)就会发现2次时间间隔是 ...

  5. 【NOI2019模拟2019.6.27】B (生成函数+整数划分dp|多项式exp)

    Description: \(1<=n,k<=1e5,mod~1e9+7\) 题解: 考虑最经典的排列dp,每次插入第\(i\)大的数,那么可以增加的逆序对个数是\(0-i-1\). 不难 ...

  6. Spring声明式事务的实现方式选择(JDK动态代理与cglib)

    1.简介 Spring声明式事务的具体实现方式是动态决定的,与具体配置.以及事务代理对象是否实现接口等有关. 2.使用JDK动态代理的情况 在满足下面两个条件时,Spring会选择JDK动态代理作为声 ...

  7. 11.RabbitMQ单机集群

    RabbitMQ集群设计用于完成两个目标:允许消费者和生产者在RabbitMQ节点崩溃的情况下继续运行,以及通过添加更多的节点来扩展消息通信的吞吐量. RabbitMQ会始终记录以下四种类型的内部元数 ...

  8. Java--下大雪模拟

    package firstpack; import java.awt.*; public class MyStar { public static void main(String[] args) { ...

  9. NYOJ - 35 表达式求值 分类: NYOJ 2015-03-18 10:33 31人阅读 评论(0) 收藏

    #include<iostream> #include<string> #include<stack> #include<cstdio> using n ...

  10. 专题:“find -perm”

    Search for files which have read and write permission for their owner, and group, but which other us ...