[2018HN省队集训D1T1] Tree
[2018HN省队集训D1T1] Tree
题意
给定一棵带点权树, 要求支持下面三种操作:
1 root
将root
设为根.2 u v d
将以 \(\operatorname{LCA} (u,v)\) 为根的子树中的点权值加上 \(d\).3 u
查询以 \(u\) 为根的子树中的点的权值之和.
初始时根为 \(1\).
\(n,q\le3\times 10^5\)
时限 \(1\texttt{s}\).
题解
垃圾卡常题毁我青春
写这个题解主要是存板子的...毕竟LCT上比较科学优雅地实现LCA需要改板子...但是我的LCT长得比较滑稽没几个人写得和我一样
考场上主要思路是当成把换根子树修改和换根LCA分开算, 子树修改可以对DFS序建线段树解决. 具体做法是分类讨论新根 \(p\) /原来的根 \(r\) /要修改的子树的根 \(u\) 三个点的位置关系. 若 \(u\) 不在 \(p\) 到 \(r\) 的路径上, 那么直接修改 \(u\) 在以 \(r\) 为根的子树即可. 否则设 \(u\rightarrow v \leadsto r\), 那么除了 \(v\) 的子树之外的所有点都要修改, 分两段解决或者先整体加再子树减也可以.
换根LCA是LCT的标准操作. 比较科学地求LCA需要在 Access
的时候返回最后一次连接的虚边的父亲侧结点, 就可以两次 Access
求出LCA了.
考场上打完没过样例发现Access的时候把虚子树连到Splay左儿子去了囧...
然后这题数据丧病地出到了 3e5 所以需要常数优化一下比如加个快读3e5的读入量还不加快读显然是自己作死吧
参考代码
#include <bits/stdc++.h>
const int MAXE=1e6+10;
const int MAXV=3e5+10;
typedef long long intEx;
struct Edge{
int from;
int to;
Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* top=E;
struct LCT{
#define lch chd[0]
#define rch chd[1]
#define kch chd[k]
#define xch chd[k^1]
struct Node{
int id;
bool rev;
Node* prt;
Node* pprt;
Node* chd[2];
Node(int id):id(id),rev(false),prt(NULL),pprt(NULL),chd{NULL,NULL}{}
inline void Flip(){
if(this!=NULL){
this->rev=!this->rev;
std::swap(this->lch,this->rch);
}
}
inline void PushDown(){
if(this!=NULL&&this->rev){
this->lch->Flip();
this->rch->Flip();
this->rev=false;
}
}
};
std::vector<Node*> N;
LCT(int n):N(n+1){
for(int i=1;i<=n;i++)
N[i]=new Node(i);
}
inline void Rotate(Node* root,int k){
Node* tmp=root->xch;
root->PushDown();
tmp->PushDown();
tmp->prt=root->prt;
if(root->prt==NULL){
tmp->pprt=root->pprt;
root->pprt=NULL;
}
else if(root->prt->lch==root)
root->prt->lch=tmp;
else
root->prt->rch=tmp;
root->xch=tmp->kch;
if(root->xch!=NULL)
root->xch->prt=root;
tmp->kch=root;
root->prt=tmp;
}
inline void Splay(Node* root){
while(root->prt!=NULL){
int k=root->prt->lch==root;
if(root->prt->prt==NULL)
Rotate(root->prt,k);
else{
int d=root->prt->prt->lch==root->prt;
Rotate(k==d?root->prt->prt:root->prt,k);
Rotate(root->prt,d);
}
}
}
inline void Expose(Node* root){
Splay(root);
root->PushDown();
if(root->rch!=NULL){
root->rch->prt=NULL;
root->rch->pprt=root;
root->rch=NULL;
}
}
inline Node* Access(Node* root){
Expose(root);
Node* ret=root;
while(root->pprt!=NULL){
ret=root->pprt;
Expose(root->pprt);
root->pprt->rch=root;
root->prt=root->pprt;
root->pprt=NULL;
Splay(root);
}
return ret;
}
inline void Evert(Node* root){
Access(root);
Splay(root);
root->Flip();
}
inline void Evert(int root){
Evert(N[root]);
}
inline void Link(int prt,int son){
Evert(N[son]);
N[son]->pprt=N[prt];
}
inline int LCA(int x,int y){
Access(N[x]);
return Access(N[y])->id;
}
#undef lch
#undef rch
#undef kch
#undef xch
};
struct Node{
int l;
int r;
intEx add;
intEx sum;
Node* lch;
Node* rch;
Node(int,int);
void Maintain();
void PushDown();
intEx Query(int,int);
void Add(const intEx&);
void Add(int,int,const intEx&);
};
int n;
int q;
int clk;
int val[MAXV];
int pos[MAXV];
int dfn[MAXV];
int deep[MAXV];
int size[MAXV];
int prt[20][MAXV];
void ReadInt(int&);
void Insert(int,int);
int Ancestor(int,int);
void DFS(int,int,int);
int main(){
ReadInt(n);
ReadInt(q);
for(int i=1;i<=n;i++)
ReadInt(val[i]);
LCT* T=new LCT(n);
for(int i=1;i<n;i++){
int a,b;
ReadInt(a);
ReadInt(b);
Insert(a,b);
Insert(b,a);
T->Link(a,b);
}
DFS(1,0,0);
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j<=n;j++)
prt[i][j]=prt[i-1][prt[i-1][j]];
Node* N=new Node(1,n);
T->Evert(1);
int root=1;
for(int i=0;i<q;i++){
int t;
ReadInt(t);
if(t==1){
ReadInt(root);
T->Evert(root);
}
else if(t==2){
int a,b,d;
ReadInt(a);
ReadInt(b);
ReadInt(d);
int lca=T->LCA(a,b);
if(lca==root)
N->Add(1,n,d);
else if(deep[lca]>=deep[root])
N->Add(dfn[lca],dfn[lca]+size[lca]-1,d);
else if(Ancestor(root,deep[root]-deep[lca])==lca){
int x=Ancestor(root,deep[root]-deep[lca]-1);
N->Add(1,n,d);
N->Add(dfn[x],dfn[x]+size[x]-1,-d);
}
else
N->Add(dfn[lca],dfn[lca]+size[lca]-1,d);
}
else if(t==3){
int r;
ReadInt(r);
intEx ans=0;
if(r==root)
ans=N->Query(1,n);
else if(deep[r]>=deep[root])
ans=N->Query(dfn[r],dfn[r]+size[r]-1);
else if(Ancestor(root,deep[root]-deep[r])==r){
int x=Ancestor(root,deep[root]-deep[r]-1);
ans+=N->Query(1,n);
ans-=N->Query(dfn[x],dfn[x]+size[x]-1);
}
else
ans=N->Query(dfn[r],dfn[r]+size[r]-1);
printf("%lld\n",ans);
}
}
return 0;
}
inline int Ancestor(int cur,int k){
for(int i=0;(1<<i)<=k;i++)
if((1<<i)&k)
cur=prt[i][cur];
return cur;
}
void DFS(int root,int prt,int deep){
::size[root]=1;
::dfn[root]=++clk;
::deep[root]=deep;
::prt[0][root]=prt;
::pos[dfn[root]]=root;
for(Edge* i=head[root];i!=NULL;i=i->next){
if(i->to!=prt){
DFS(i->to,root,deep+1);
size[root]+=size[i->to];
}
}
}
inline void Insert(int from,int to){
top->from=from;
top->to=to;
top->next=head[from];
head[from]=top++;
}
Node::Node(int l,int r):l(l),r(r),add(0),lch(NULL),rch(NULL){
if(l==r)
sum=val[pos[l]];
else{
int mid=(l+r)>>1;
this->lch=new Node(l,mid);
this->rch=new Node(mid+1,r);
this->sum=this->lch->sum+this->rch->sum;
}
}
void Node::Add(int l,int r,const intEx& d){
if(l<=this->l&&this->r<=r)
this->Add(d);
else{
this->PushDown();
if(l<=this->lch->r)
this->lch->Add(l,r,d);
if(this->rch->l<=r)
this->rch->Add(l,r,d);
this->Maintain();
}
}
intEx Node::Query(int l,int r){
if(l<=this->l&&this->r<=r)
return this->sum;
else{
this->PushDown();
if(r<=this->lch->r)
return this->lch->Query(l,r);
if(this->rch->l<=l)
return this->rch->Query(l,r);
return this->lch->Query(l,r)+this->rch->Query(l,r);
}
}
void Node::Maintain(){
this->sum=this->lch->sum+this->rch->sum;
}
void Node::PushDown(){
if(this->add){
this->lch->Add(this->add);
this->rch->Add(this->add);
this->add=0;
}
}
inline void Node::Add(const intEx& d){
this->add+=d;
this->sum+=d*(r-l+1);
}
inline void ReadInt(int& target){
target=0;
int sgn=1;
register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')
sgn=-sgn;
ch=getchar();
}
while(isdigit(ch)){
target=target*10+ch-'0';
ch=getchar();
}
target*=sgn;
}
[2018HN省队集训D1T1] Tree的更多相关文章
- [2018HN省队集训D9T1] circle
[2018HN省队集训D9T1] circle 题意 给定一个 \(n\) 个点的竞赛图并在其中钦定了 \(k\) 个点, 数据保证删去钦定的 \(k\) 个点后这个图没有环. 问在不删去钦定的这 \ ...
- [2018HN省队集训D8T1] 杀毒软件
[2018HN省队集训D8T1] 杀毒软件 题意 给定一个 \(m\) 个01串的字典以及一个长度为 \(n\) 的 01? 序列. 对这个序列进行 \(q\) 次操作, 修改某个位置的字符情况以及查 ...
- [2018HN省队集训D8T3] 水果拼盘
[2018HN省队集训D8T3] 水果拼盘 题意 给定 \(n\) 个集合, 每个集合包含 \([1,m]\) 中的一些整数, 在这些集合中随机选取 \(k\) 个集合, 求这 \(k\) 个集合的并 ...
- [2018HN省队集训D6T2] girls
[2018HN省队集训D6T2] girls 题意 给定一张 \(n\) 个点 \(m\) 条边的无向图, 求选三个不同结点并使它们两两不邻接的所有方案的权值和 \(\bmod 2^{64}\) 的值 ...
- [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform
[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...
- [2018HN省队集训D5T2] party
[2018HN省队集训D5T2] party 题意 给定一棵 \(n\) 个点以 \(1\) 为根的有根树, 每个点有一个 \([1,m]\) 的权值. 有 \(q\) 个查询, 每次给定一个大小为 ...
- [2018HN省队集训D5T1] 沼泽地marshland
[2018HN省队集训D5T1] 沼泽地marshland 题意 给定一张 \(n\times n\) 的棋盘, 对于位置 \((x,y)\), 若 \(x+y\) 为奇数则可能有一个正权值. 你可以 ...
- [Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard
[Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard 题意 给定一个 \(n\times n\) 的矩阵 \(A\), (\(n\) 为奇数) , ...
- [2018HN省队集训D1T3] Or
[2018HN省队集训D1T3] Or 题意 给定 \(n\) 和 \(k\), 求长度为 \(n\) 的满足下列条件的数列的数量模 \(998244353\) 的值: 所有值在 \([1,2^k)\ ...
随机推荐
- asp.net MVC 文件流导出Excel
<form id="FormEsExcel" action="/**/ExportEsExcelData" method="post" ...
- sql语句中出现笛卡尔乘积 SQL查询入门篇
2014-12-29 凡尘工作室 阅 34985 转 95 本篇文章中,主要说明SQL中的各种连接以及使用范围,以及更进一步的解释关系代数法和关系演算法对在同一条查询的不同思路. 多表连接简介 ...
- ie,你还能再浪一点不
一个div,设置了高度,并且溢出滚动 各位观众,当点击滚动条的时候,event.target应该是什么呢? 火狐,chrome都认为是点击了div,这个也很好理解,他是div的滚动条,自然应该算div ...
- [Redis] redis数据备份恢复与持久化
数据库备份,使用save命令,将会在redis的安装目录中生成dump.rdb 例如:在我的目录下 redis/src/dump.rdb 使用命令config get dir,获取当前redis的安装 ...
- MySQL数据库的回滚失败(JAVA)
这几天在学习MySQL数据的知识,有一个小测试,用来测试数据库的提交和回滚. 刚开始的时候真的没把这个当回事,按照正常的步骤来讲的话,如下所示,加载驱动,获取数据库的连接,并且把数据库的自动提交给关闭 ...
- java 获取两个日期之间的所有日期(年月日)
前言:直接上代码 java 获取两个日期之间的所有日期(年月日) /** * 获取两个日期之间的日期,包括开始结束日期 * @param start 开始日期 * @param end 结束日期 * ...
- javaMail 邮件发送和接收示例,支持正文图片、html、附件(转)
转自:https://blog.csdn.net/star_fly4/article/details/52037587 一.RFC882文档简单说明 RFC882文档规定了如何编写一封简单的邮件(纯文 ...
- MVC 导出Execl 的总结几种方式 (四)
这种方式我个人还是比较喜欢的,使用部分视图的方式,导出Execl 这样在编辑样式上也是很方便的 第一步: 编辑导出视图页 @using H5UpdateImage.Models; @{ Layout ...
- asp.ne如何使用javascript去验证客户端信息,如果验证成功则送往服务器端处理,否则在客户端提示用户(不返回到服务器端处理)
一.问题 在网站一般都有很多地方需要用户去填写一些信息,然后用户点击提交,将信息送往后台储存到数据库中.在这一个过程我以前做法直接在button的click事件中来判断用户输入的数据是否完整和合法,虽 ...
- 12:计算2的N次方
12:计算2的N次方 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB 描述 任意给定一个正整数N(N<=100),计算2的n次方的值. 输入 输入一个正整数N ...