题面
题目描述
给你一棵树,边权为 111,有点权。
需要支持两个操作:
1 x y z:表示把树上 xxx 到 yyy 这条简单路径的所有点点权都加上 zzz。2 x y:表示查询与点 xxx 距离小于等于 111 的所有点里面的第 yyy 小点权。
输入格式
第一行两个整数 n,mn,mn,m。
第二行 nnn 个整数表示每个点的点权。
之后 n−1n-1n−1 行,每行两个整数 x,yx,yx,y 表示 xxx 和 yyy 之间连有一条边。
之后 mmm 行,每行为 1 x y z 或者 2 x y 形式,意义如上述。
输出格式
对每个 2 操作输出一行,每行一个整数表示答案。
数据保证每次询问都存在答案。
输入输出样例 #1
输入 #1
5 5
3 4 3 1 3
1 2
1 3
2 4
3 5
2 1 3
2 1 1
1 1 1 1
2 1 3
1 4 1 1
输出 #1
4
3
4
说明/提示
subtask 1:20%20\%20% n,m≤1000n,m\leq 1000n,m≤1000。
subtask 2:10%10\%10% 树为一条链。
subtask 3:20%20\%20% n,m≤105n,m\leq 10^5n,m≤105。
subtask 4:30%30\%30% n,m≤4×105n,m\leq 4\times 10^5n,m≤4×105。
subtask 5:20%20\%20% n,m≤106n,m\leq 10^6n,m≤106。
对于 100%100\%100% 的数据,1≤n,m≤1061\leq n,m\leq 10^61≤n,m≤106,0\\leq 每次加的数 ≤2000\leq 2000≤2000,0\\leq 初始的点权 ≤2000\leq 2000≤2000。
题解
opt1
看到这题,对于树上操作,我想到树剖 ,操作1显然树剖 后用线段树 / 树状数组+差分维护每个点的值
opt2
看到
2 x y:表示查询与点 xxx 距离小于等于 111 的所有点里面的 第 yyy 小点权。
显然可以用平衡树 去维护第y小
考虑有哪些点会影响答案,可见只有父节点 ,重儿子 和轻儿子集合 还有它本身 ,那么我们对于每一个节点建一颗平衡树去维护答案
考虑在修改之后会有什么影响,由于树链剖分的性质,一条链上的修改顶多改log(n)次 ,而每个点只有一个重儿子 ,在查询的时候加回来就行 了,没必要在平衡树上维护,用上文的树状数组维护,维护父节点、自己同理 。那么我们的平衡树就只用维护轻儿子们了
分析复杂度,暴力修改有树状数组的O(logn)+树剖修改轻儿子跳链的O(logn)∗平衡树的O(logn)O(\log n)+树剖修改轻儿子跳链的O(\log n)*平衡树的O(logn)O(logn)+树剖修改轻儿子跳链的O(logn)∗平衡树的O(logn)为O(log2n)O(\log^2 n)O(log2n)
查询就是一个单老哥
整个时间复杂度O(qlog2n)O(q\log^2 n)O(qlog2n)好悬
代码:
cpp
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
int n,m,r,cnt;
vector<int> e[1000001];
int son[1000001],top[1000001],fa[1000001],id[1000001];
int dep[1000001],siz[1000001];
int w[1000001],res;
struct node{
int u,id;
bool operator<(const node& y)const{
return u!=y.u?u<y.u:id<y.id;
}
};
int lowbit(int x){
return x&(-x);
}
tree<node ,null_type,less<node>,rb_tree_tag,tree_order_statistics_node_update> tr[1000001];
struct shuzu{
int tr[1000003];
void add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i)){
tr[i]+=y;
}
}
int cha(int x){
int sum=0;
for(int i=x;i>=1;i-=lowbit(i)){
sum+=tr[i];
}
return sum;
}
void addqu(int x,int y,int c){
add(x,c);
add(y+1,-c);
}
}tre;
void dfs1(int u,int f,int deep){
dep[u]=deep;
fa[u]=f;
siz[u]=1;
int maxson=-1;
for(int i:e[u]){
if(i==f){
continue;
}
dfs1(i,u,deep+1);
siz[u]+=siz[i];
if(siz[i]>maxson){
son[u]=i;
maxson=siz[i];
}
}
}
void dfs2(int u,int topf){
id[u]=++cnt;
tre.addqu(cnt,cnt,w[u]);
top[u]=topf;
if(!son[u]){
return ;
}
dfs2(son[u],topf);
for(int i:e[u]){
if(i==fa[u]||i==son[u]){
continue;
}
tr[u].insert({w[i],i});
dfs2(i,i);
}
}
void treeadd(int x,int y,int c){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
}
int tp=top[x],now=x;
x=fa[top[x]];
tr[x].erase({w[tp],tp});
tre.addqu(id[tp],id[now],c);
w[tp]=tre.cha(id[tp]);
tr[x].insert({w[tp],tp});
}
if(dep[x]<dep[y]){
swap(x,y);
}
tre.addqu(id[y],id[x],c);
if(top[y]!=y||!fa[y]){
return ;
}
tr[fa[y]].erase({w[y],y});
w[y]=tre.cha(id[y]);
tr[fa[y]].insert({w[y],y});
}
void join(int u,int s){
if(s)tr[u].insert({tre.cha(id[s]),s});
}
void del(int u,int s){
if(s)tr[u].erase({tre.cha(id[s]),s});
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i];
}
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
// cout<<"OK"<<endl;
dfs1(1,0,1);
dfs2(1,1);
while(m--){
int opt,x,y,z;
cin>>opt>>x>>y;
if(opt==1){
cin>>z;
treeadd(x,y,z);
}
else{
join(x,x);
join(x,son[x]);
join(x,fa[x]);
cout<<tr[x].find_by_order(y-1)->u<<"\n";
del(x,x);
del(x,son[x]);
del(x,fa[x]);
}
}
}
结
本篇题解与封面没有任何关系,这题解不是五彩斑斓的世界