题目
n(n<=1e5)个点的树,每一条边都有一个权值w(w<=1e4),代表走过它所需要的时间花费,
每个点上面有初始有一个苹果,每个苹果有一个成熟度。
m(m<=1e5)条操作,操作分两种。
1 u x(1≤u≤n,1≤x≤10000):在某一个点u新出现了一个成熟度为x的苹果。
2 u x y(1≤u≤n,1≤x≤y≤10000):询问米咔从点u出发,去摘一个成熟度在[x,y]范围内的苹果并回到u的最小时间花费。
思路来源
https://blog.nowcoder.net/n/a1f1bf12bff446e085143093d741ec4d
题解
考虑如果树是随机的,树高logn,则每个点往上跳只有logn个祖先,
动态开点线段树,点i的线段树对应值域[1,10000],维护i的子树里成熟度为v的最短距离,
则n棵线段树总共的点数是nlogn,空间是nlogn*log10000
修改时,去修改logn个祖先的值;
查询时,去logn个祖先里查询,答案一定会在某个lca被枚举到,答案是v到lca的最小值+u到lca的距离,
如果u和v路径有重合的部分也没事,lca(u,v)会枚举到那个不重合时的答案
再考虑树型不随机怎么做,
首先,点分治,再分治每个重心的时候,考虑将重心的父亲设为上一个重心
由于点分治log次即可到底,所以根据重心重建的点分树的树高是log的
这样,每个点u,就会被包含在log棵分治的树内,
设答案的那一端节点是v,从重心往上跳上一次重心,这样不断跳par,
从某个重心lca同时包含u和v起,再往上的树就会都包含,而答案就会在这个lca被统计到
代码
#pragma GCC optimize(3,"inline","Ofast")
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1e5+10;
int head[N],cnt;
struct edge{int v,nex;ll w;}e[2*N];
void add(int u,int v,ll w){e[++cnt]=edge{v,head[u],w};head[u]=cnt;}
int siz,f[N],sz[N],d[N],rt;
bool vis[N];
//level[i]:表示如果i是点分治的根 这是在第几层
//par[i]:只对重心建树 点分树
//dis[i][j]:表示在点分树第i层中 j号节点距第i层的根的距离
int n,m,u,v,w,x,y,op;
int a[N],dis[20][N],level[N],par[N];
int root[N];
void init(int n){
cnt=0;
for(int i=1;i<=n;++i){
head[i]=vis[i]=0;
}
}
//找下一次的重心rt
void getrt(int u,int fa){
f[u]=0;sz[u]=1;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(v==fa||vis[v])continue;
getrt(v,u);
f[u]=max(f[u],sz[v]);
sz[u]+=sz[v];
}
f[u]=max(f[u],siz-sz[u]);
if(f[u]<f[rt])rt=u;
}
//计算重心u到子树内每个点的距离
void getdis(int u,int fa,int kth){
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
ll w=e[i].w;
if(v==fa||vis[v])continue;
dis[kth][v]=dis[kth][u]+w;
getdis(v,u,kth);
}
}
void dfs(int u){
vis[u]=1;
getdis(u,u,level[u]);
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
ll w=e[i].w;
if(vis[v])continue;
getrt(v,u);//获得正确的sz[v]
siz=sz[v];rt=0;
getrt(v,u);
par[rt]=u;
level[rt]=level[u]+1;
dfs(rt);
}
}
struct Tree{
int c;
struct node{
int l,r,mn;
node():l(0),r(0),mn(INF){}
}e[N*200];
Tree(){
c=0;
}
void newnode(int &p){
p=++c;
e[p].l=e[p].r=0;
e[p].mn=INF;
}
void upd(int &p,int l,int r,int x,int v){
if(!p){
newnode(p);
}
if(l==r){
e[p].mn=min(e[p].mn,v);
return;
}
int mid=(l+r)/2;
if(x<=mid)upd(e[p].l,l,mid,x,v);
else upd(e[p].r,mid+1,r,x,v);
e[p].mn=min(e[e[p].l].mn,e[e[p].r].mn);
}
int ask(int p,int l,int r,int ql,int qr){
if(ql>qr)return INF;
if(!p)return INF;
if(ql<=l&&r<=qr){
return e[p].mn;
}
int mid=(l+r)/2,ans=INF;
if(ql<=mid)ans=min(ans,ask(e[p].l,l,mid,ql,qr));
if(qr>mid)ans=min(ans,ask(e[p].r,mid+1,r,ql,qr));
return ans;
}
}tr;
void ins(int u,int x){
for(int v=u;v;v=par[v]){
tr.upd(root[v],1,10000,x,dis[level[v]][u]);
}
}
int ask(int u,int x,int y){
int ans=INF;
for(int v=u;v;v=par[v]){
ans=min(ans,tr.ask(root[v],1,10000,x,y)+dis[level[v]][u]);
}
return ans==INF?-1:2*ans;
}
int main(){
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for(int i=1;i<n;++i){
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
f[0]=siz=n;rt=0;
getrt(1,0);
dfs(rt);
for(int i=1;i<=n;++i){
ins(i,a[i]);
}
while(m--){
scanf("%d",&op);
if(op==1){
scanf("%d%d",&u,&x);
ins(u,x);
}
else{
scanf("%d%d%d",&u,&x,&y);
int v=ask(u,x,y);
printf("%d\n",v);
}
}
return 0;
}