1

给定一棵n个点的树,点有正点权,支持修改点权,查询带权直径。

普通的 DP 需要维护最长链和次长链,带修改的话不容易动态维护。因此我们采用另一种方法:找两次最远点。

LCT 维护的是:对于一棵 splay 的某个点,维护它 LCT 子树的答案,也就是对应的这条链的链顶的子树内,从这条链的端点出发的最长链(可能有点绕,很多 LCT 维护信息都是这么维护的)。为了维护这个信息,我们还需要维护每个虚儿子出发向(原树)子树的最长链,为了支持 access 用一个堆维护这些最长链,用堆顶就可以 push_up。

/*
lmax:从链顶出发的最长链
rmax:从链尾出发的最长链
sum:splay子树点权和
s:虚儿子最长链的堆
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
struct node{ll w;int x;
}lmax[N],rmax[N],Max[N];
struct edge{int to,next;
}ed[N<<1];multiset <node> s[N];
int fa[N],son[N][2],a[N],sz,head[N];
ll sum[N];inline void add_edge(int from,int to)
{ed[++sz].to=to;ed[sz].next=head[from];head[from]=sz;
}node operator + (node a,ll x)
{a.w+=x;return a;
}
bool operator < (node a,node b)
{if(a.w==b.w) return a.x<b.x;return a.w<b.w;
}
inline int read()
{char c=getchar();int x=0,flag=1;while(!isdigit(c)){if(c=='-') flag=-1;c=getchar();}while(isdigit(c)) x=x*10+c-'0',c=getchar();return x*flag;
}
void dfs(int u,int ff)
{for(int i=head[u];i;i=ed[i].next){int v=ed[i].to;if(v==ff) continue;fa[v]=u;dfs(v,u);s[u].insert(lmax[v]);}lmax[u]=rmax[u]=*s[u].rbegin()+a[u];Max[u]=*s[u].rbegin();
}
bool nroot(int x)
{return son[fa[x]][0]==x||son[fa[x]][1]==x;
}
void push_up(int x)
{int ls=son[x][0],rs=son[x][1];lmax[x]=max(lmax[ls],max(lmax[rs],Max[x])+sum[ls]+a[x]);rmax[x]=max(rmax[rs],max(rmax[ls],Max[x])+sum[rs]+a[x]);sum[x]=sum[ls]+sum[rs]+a[x];
}
void rotate(int x)
{int y=fa[x],z=fa[y],kind=x==son[y][0];int tmp=nroot(y);son[y][!kind]=son[x][kind];fa[son[x][kind]]=y;son[x][kind]=y;fa[y]=x;fa[x]=z;if(tmp) {if(y==son[z][0]) son[z][0]=x;else son[z][1]=x;}push_up(y);
}
void splay(int x)
{while(nroot(x)){int y=fa[x],z=fa[y];if(nroot(y)) {if((x==son[y][0])^(y==son[z][0])) rotate(x);else rotate(y);}rotate(x);}push_up(x);
}
void access(int x)
{for(int y=0;x;x=fa[y=x]){splay(x);if(son[x][1]) s[x].insert(lmax[son[x][1]]);son[x][1]=y;if(y) s[x].erase(s[x].find(lmax[y]));Max[x]=*s[x].rbegin();push_up(x);}
}
int main()
{int n=read(),m=read();for(int i=1;i<=n;i++) a[i]=read(),s[i].insert((node){0,i});for(int i=1;i<n;i++) {int u=read(),v=read();add_edge(u,v),add_edge(v,u);}dfs(1,0);while(m--){int x=read(),y=read();access(x),splay(x);a[x]=y;push_up(x);x=lmax[x].x;access(x);splay(x);cout<<rmax[x].w<<'\n';}return 0;
}
/*by DT_Kang*/

2

我们的 CPU 遭到攻击
维护一个森林,边有边权,支持 Link,Cut,反转一个点的颜色(黑白),查询某个联通块的所有黑点到某个点的距离之和。

这个题同样的,我们 splay 上的点维护 LCT 子树到链头/尾的答案(因为要支持 makeroot)。为了维护答案,还有维护黑点数量,边权之和。对于每个点,还要维护虚子树的黑点之和,以及虚子树答案。

/*
size:LCT子树黑点数
sizei:虚子树黑点数
ansi:虚儿子的答案
ansl:到链顶答案
sum:splay子树边权和
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
unordered_map <ll,int> mp;
int son[400010][2],size[400010],sizei[400010],col[400010],val[400010],tot,fa[400010],tmp[400010],rev[400010];
ll ansl[400010],ansr[400010],ansi[400010],sum[400010];
inline int read()
{char c=getchar();int x=0,flag=1;while(!isdigit(c)){if(c=='-') flag=-1;c=getchar();}while(isdigit(c)) x=x*10+c-'0',c=getchar();return x*flag;
}
bool notroot(int x)
{return (son[fa[x]][0]==x)||(son[fa[x]][1]==x);
}
void reverse(int x)
{rev[x]^=1;swap(son[x][0],son[x][1]);swap(ansl[x],ansr[x]);
}
void push_up(int x)
{int ls=son[x][0],rs=son[x][1];sum[x]=sum[ls]+sum[rs]+val[x];size[x]=size[ls]+size[rs]+col[x]+sizei[x];ansl[x]=ansl[ls]+ansl[rs]+(sum[ls]+val[x])*(size[rs]+sizei[x]+col[x])+ansi[x];ansr[x]=ansr[rs]+ansr[ls]+(sum[rs]+val[x])*(size[ls]+sizei[x]+col[x])+ansi[x];
}
void push_down(int x)
{if(rev[x]){if(son[x][0]) reverse(son[x][0]);if(son[x][1]) reverse(son[x][1]);rev[x]=0;}
}
void rotate(int x)
{int y=fa[x],z=fa[y],kind= x==son[y][0];int tmp=notroot(y);son[y][!kind]=son[x][kind];fa[son[x][kind]]=y;fa[y]=x;son[x][kind]=y;fa[x]=z;if(tmp){if(y==son[z][0]) son[z][0]=x;else son[z][1]=x;}push_up(y);push_up(x);
}
void splay(int x)
{int y=x,p=0;while(notroot(y)) tmp[++p]=y,y=fa[y];tmp[++p]=y;while(p--) push_down(tmp[p+1]);while(notroot(x)){int y=fa[x],z=fa[y];if(notroot(y)){if((x==son[y][0])^(y==son[z][0])) rotate(x);else rotate(y);}rotate(x);}
}
void access(int x)
{for(int y=0;x;x=fa[y=x]){splay(x);int tmp=son[x][1];sizei[x]+=size[tmp];ansi[x]+=ansl[tmp];son[x][1]=y;sizei[x]-=size[y];ansi[x]-=ansl[y];push_up(x);}
}
void makeroot(int x)
{access(x),splay(x);reverse(x);
}
void Link(int x,int y)
{makeroot(x),makeroot(y);fa[x]=y,sizei[y]+=size[x],ansi[y]+=ansl[x];push_up(y);
}
void Cut(int x,int y)
{makeroot(x),access(y),splay(y);son[y][0]=fa[x]=0;push_up(y);
}
void add_edge(int x,int y,int w)
{tot++,val[tot]=w;Link(x,tot),Link(tot,y);mp[1000000ll*x+y]=mp[1000000ll*y+x]=tot;
}
int main()
{int n=read(),m=read(),k=read();tot=n;for(int i=1;i<=m;i++){int u=read(),v=read(),w=read();add_edge(u,v,w);}while(k--){char s[5];scanf("%s",s);char c=s[0];int x,y,w,tmp;if(c=='L') x=read(),y=read(),w=read(),add_edge(x,y,w);else if(c=='C') x=read(),y=read(),tmp=mp[1000000ll*x+y],Cut(x,tmp),Cut(tmp,y);else if(c=='F') x=read(),access(x),splay(x),col[x]^=1,push_up(x);else //if(c=='Q') x=read(),makeroot(x),splay(x),cout<<ansl[x]<<'\n';}return 0;
}
/*by DT_Kang*/

3

Qtree7
维护三种操作:修改点的颜色(黑白),修改点的权值,查询 x 所在同色联通块的最大权值。

维护同色联通块的小技巧:我们把点的颜色放在它到父亲这条边上,同色边放在一棵 LCT 里。容易发现一个联通块只有根节点颜色不同。注意特判整棵树的根。

这个 LCT 是不需要换根的,注意 Link,Cut 的写法。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define ld long double
using namespace std;
const int N=200020,inf=1e9;
typedef pair <int,int> P;
struct edge {int to,next;
}ed[N<<2];
int head[N],col[N],w[N],sz;
void add_edge(int from,int to)
{ed[++sz].to=to;ed[sz].next=head[from];head[from]=sz;
}
int read()
{int x=0;char c=getchar(),flag='+';while(!isdigit(c)) flag=c,c=getchar();while(isdigit(c)) x=x*10+c-'0',c=getchar();return flag=='-'?-x:x;
}
struct Heap
{priority_queue <int> q1,q2;void push(int x) {q1.push(x);}void erase(int x) {q2.push(x);}int top() {while(q2.size()&&q1.top()==q2.top()) q2.pop(),q1.pop();return q1.top();}
};
struct LCT
{int son[N][2],fa[N],mx[N];Heap si[N];bool nroot(int x) {return son[fa[x]][0]==x||son[fa[x]][1]==x;}void up(int x) {mx[x]=max(max(mx[son[x][0]],mx[son[x][1]]),max(w[x],si[x].top()));}void rotate(int x){int y=fa[x],z=fa[y],kind=x==son[y][0],tmp=nroot(y);son[y][!kind]=son[x][kind];fa[son[x][kind]]=y;son[x][kind]=y;fa[y]=x,fa[x]=z;if(tmp){if(y==son[z][0]) son[z][0]=x;else son[z][1]=x;}up(y),up(x);}void splay(int x){while(nroot(x)){int y=fa[x],z=fa[y];if(nroot(y)) {if(x==son[y][0]^y==son[z][0]) rotate(x);else rotate(y);}rotate(x);}}void access(int x){for(int y=0;x;x=fa[y=x]){splay(x);if(son[x][1]) si[x].push(mx[son[x][1]]);if(y) si[x].erase(mx[y]);son[x][1]=y,up(x);}}void Cut(int x,int y){if(!y) return;access(x),splay(x);son[x][0]=fa[son[x][0]]=0;up(x);}void Link(int x,int y) {if(!y) return;access(x),splay(x);access(y),splay(y);fa[x]=y;si[y].push(mx[x]);up(y);}int find(int x){access(x),splay(x);int now=x;while(son[now][0]) now=son[now][0];return now;}
};
LCT T[2];
int fa[N];
void dfs(int u,int ff)
{for(int i=head[u];i;i=ed[i].next){int v=ed[i].to;if(v==ff) continue;T[col[v]].Link(v,u);fa[v]=u;dfs(v,u);}
}
int main()
{int n=read();for(int i=1;i<n;i++){int u=read(),v=read();add_edge(u,v),add_edge(v,u);}T[0].mx[0]=T[1].mx[0]=-inf;for(int i=1;i<=n;i++) col[i]=read(),T[0].si[i].push(-inf),T[1].si[i].push(-inf),T[0].up(i),T[1].up(i);for(int i=1;i<=n;i++) w[i]=read();dfs(1,0);int m=read();for(int i=1;i<=m;i++){int opt=read(),x=read(),c=col[x];if(opt==0) {int tmp=T[c].find(x);T[c].access(x);T[c].splay(tmp);if(c==col[tmp]) cout<<T[c].mx[tmp]<<'\n';else cout<<T[c].mx[T[c].son[tmp][1]]<<'\n';}else if(opt==1){T[c].Cut(x,fa[x]);col[x]^=1,c^=1;T[c].Link(x,fa[x]);}else {int val=read();T[c].access(x),T[c].splay(x);w[x]=val;T[c].up(x);}}return 0;
}
/*by DT_Kang*/

一类用 LCT 维护信息的题目相关推荐

  1. 【uoj#207】共价大爷游长沙 随机化+LCT维护子树信息

    题目描述 给出一棵树和一个点对集合S,多次改变这棵树的形态.在集合中加入或删除点对,或询问集合内的每组点对之间的路径是否都经过某条给定边. 输入 输入的第一行包含一个整数 id,表示测试数据编号,如第 ...

  2. LCT维护子树信息(BZOJ4530:[BJOI2014]大融合)

    题面 没有权限号的可以去LOJ Sol 大家都知道,\(LCT\)上有许多实边和虚边 实边就是每棵\(Splay\)上的既认父亲又认儿子的边 虚边就是\(Splay\)和\(Splay\)之间只认父亲 ...

  3. bzoj 4530 [Bjoi2014]大融合——LCT维护子树信息

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4530 LCT维护子树 siz .设 sm[ ] 表示轻儿子的 siz 和+1(1是自己的si ...

  4. LOJ 121 「离线可过」动态图连通性——LCT维护删除时间最大生成树 / 线段树分治...

    题目:https://loj.ac/problem/121 离线,LCT维护删除时间最大生成树即可.注意没有被删的边的删除时间是 m+1 . 回收删掉的边的节点的话,空间就可以只开 n*2 了. #i ...

  5. P4172 [WC2006]水管局长 LCT维护最小生成树

    \(\color{#0066ff}{ 题目描述 }\) SC 省 MY 市有着庞大的地下水管网络,嘟嘟是 MY 市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的 ...

  6. 评价类(观点)题和理解类题目的辨析 区别一:审题干信息和题目要求。

    2020年国考申论备考:评价类(观点)题和理解类题目的辨析 区别一:审题干信息和题目要求. 一个男生和一个女生的区别,从外在生理特征便可以进行区分,这些就是"显性区别".同样,一般 ...

  7. bzoj 4736: 温暖会指引我们前行 (LCT 维护最大生成树)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4736 题面: 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出 ...

  8. [BZOJ3669] [NOI2004] 魔法森林 LCT维护最小生成树

    题面 一开始看到这道题虽然知道是跟LCT维护最小生成树相关的但是没有可以的去想. 感觉可以先二分一下总的精灵数,但是感觉不太好做. 又感觉可以只二分一种精灵,用最小生成树算另一种精灵,但是和似乎不单调 ...

  9. BZOJ3514 Codechef MARCH14 GERALD07加强版 LCT维护最大生成树 主席树

    题面 考虑没有询问,直接给你一个图问联通块怎么做. 并查集是吧. 现在想要动态地做,那么应该要用LCT. 考虑新加进来一条边,想要让它能够减少一个联通块的条件就是现在边的两个端点还没有联通. 如果联通 ...

最新文章

  1. 利用numpy对像素点进行操作
  2. mybatis的一些基础问题
  3. wordcloud用来制作中文词云
  4. Python学习笔记(1):用户输入,终端输入
  5. Bootstrap HTML编码规范之引入 CSS 和 JavaScript 文件
  6. Python - 浅谈Python的编译与反编译
  7. 1041. 考试座位号(15)-PAT乙级真题
  8. 推荐几个资源搜索网站
  9. 网易社招面试几轮?网易java社招面试题分享
  10. 阿里云操作系统——飞天(Apsaras)
  11. NFT因无意义而美丽
  12. 在VM虚拟机下安装win7系统
  13. 数据库Java项目:在线租房出租房屋系统(java+springboot+maven+mysql)
  14. Knative v1.0.x安装全过程
  15. 闭关备战python面试50天,“啃完”这份python面试宝典,我终于四面拿下阿里offer
  16. 【MySql】简明笔记
  17. mock server 挡板服务搭建
  18. python爬虫浏览器伪装和设置代理ip
  19. TreeMap、二叉树
  20. windows 10 python 3.7.9 install rosbag

热门文章

  1. candence的图纸大小设置_AutoCAD中怎样自己设置图纸大小?
  2. 简易爬虫-利用Python爬虫爬取圣墟小说到本地
  3. 精尽Spring MVC源码分析 - 一个请求的旅行过程
  4. GIS经纬度坐标转换为unity3D的世界坐标
  5. 服务器共享出去的文件显示脱机,Windows7中共享文件夹的脱机设置
  6. Arch Linux 安装Google Earth
  7. echarts 添加百分号
  8. echarts如何给柱状图设置渐变色
  9. 达梦客户端工具的使用
  10. 使用加密锁加密Unity工程插件源码