【HNOI2015】开店
动态点分治
题目大意:
给定一棵树,边带边权,点带点权。
每次给出 $u,l,r$,需要回答所有点权在 $l\sim r$ 之间的点到 $u$ 的树上距离之和。
强制在线。
题解:
这map
常数真的是大……
建出原树的点分树。
考虑每个查询,都在点分树上直接跳父亲,并求出当前连通块内的点到 $u$ 的距离之和。
建点分树时,我们求出每个连通块中,所有到当前根的路径的信息(结点的点权,到根路径长度),并需要存储根的每个儿子的子树信息。按照点权排序,对到根路径长度求前缀和。
查询的时候,对于一对 $l,r$,我们直接在对应的信息上二分就可以了。
考虑具体的查询过程。
对每个点,其到 $u$ 的距离为其到根的距离,加上 $u$ 到根的距离。这个都可以直接二分统计。
然后还要减去 $u$ 本身所在子树中算重的贡献。
建点分树的时间复杂度为 $O(n\log^2n)$,单次查询的复杂度为 $O(\log^2n)$。
总时间复杂度 $O((n+m)\log^2 n)$,空间复杂度 $O(n\log n)$。
Code:
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
using namespace std;
char buf[(int)1e8],*ss=buf;
inline int init(){buf[fread(buf,1,(int)1e8-1,stdin)]='\n';fclose(stdin);return 0;}
const int __START__=init();
inline int readint(){
int d=0;
while(!isdigit(*ss))++ss;
while(isdigit(*ss))d=d*10+(*ss++^'0');
return d;
}
const int N=1.6e5;
typedef long long LL;
typedef vector<pair<int,LL> >Data;
int n,m,age[N],head[N],all,rt,mxd[N],sz[N],lim,cnt;
bool vis[N];
LL ans;
struct edge{
int to,nxt,dis;
}e[N<<1];
void getrt(int now,int pre){
sz[now]=1,mxd[now]=0;
for(int i=head[now];i;i=e[i].nxt)
if(!vis[e[i].to]&&e[i].to!=pre){
getrt(e[i].to,now);
sz[now]+=sz[e[i].to];
mxd[now]=max(mxd[now],sz[e[i].to]);
}
mxd[now]=max(mxd[now],all-sz[now]);
if(!rt||mxd[now]<mxd[rt])rt=now;
}
Data f[N*22];
int nf=0;
int fa[N],id_rt[N],ceng_rt[N];
int nw_ceng;
int dist[19][N],mp[19][N];
pair<int,LL>get_info(int id,int l,int r){
if(f[id].empty()||f[id].front().first>r)return make_pair(0,0LL);
int itr=lower_bound(f[id].begin(),f[id].end(),make_pair(r+1,0LL))-f[id].begin()-1;
int itl=lower_bound(f[id].begin(),f[id].end(),make_pair(l,0LL))-f[id].begin()-1;
return make_pair(itr-itl,f[id][itr].second-(itl>=0?f[id][itl].second:0));
}
void dfs(int now,int pre,Data&vec,int deep,const int&rt,const int&id){
vec.push_back(make_pair(age[now],deep));
dist[nw_ceng][now]=deep;
mp[nw_ceng][now]=id;
for(int i=head[now];i;i=e[i].nxt)
if(e[i].to!=pre&&!vis[e[i].to])
dfs(e[i].to,now,vec,deep+e[i].dis,rt,id);
}
void doit(int now){
int&id=id_rt[now];id=++nf;
for(int i=head[now];i;i=e[i].nxt)if(!vis[e[i].to]){
int x=++nf;
dfs(e[i].to,now,f[x],e[i].dis,now,x);
sort(f[x].begin(),f[x].end());
for(int i=0;i<f[x].size();++i)
f[id].push_back(f[x][i]);
for(int i=1;i<f[x].size();++i)
f[x][i].second+=f[x][i-1].second;
}
f[id].push_back(make_pair(age[now],0));
sort(f[id].begin(),f[id].end());
for(int i=1;i<f[id].size();++i)
f[id][i].second+=f[id][i-1].second;
}
void build(int now,int ceng){
ceng_rt[now]=ceng;
vis[now]=1;
nw_ceng=ceng;
doit(now);
const int sm=all;
for(int i=head[now];i;i=e[i].nxt)
if(!vis[e[i].to]){
all=sz[e[i].to]<sz[now]?sz[e[i].to]:sm-sz[now];
rt=0;
getrt(e[i].to,now);
fa[rt]=now;
build(rt,ceng+1);
}
}
void make_tree(){
all=n;rt=0;
getrt(1,0);
build(rt,0);
}
int main(){
n=readint(),m=readint(),lim=readint();
for(int i=1;i<=n;++i)age[i]=readint();
for(int i=1;i<n;++i){
int u=readint(),v=readint(),w=readint();
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}
make_tree();
while(m--){
int u=readint(),l=readint(),r=readint();
l=(l+ans)%lim,r=(r+ans)%lim;
if(l>r)swap(l,r);
ans=0;
for(int x=u;x;x=fa[x]){
int c=ceng_rt[x];
pair<int,LL>al=get_info(id_rt[x],l,r);
pair<int,LL>sm=(x==u)?make_pair(0,0LL):get_info(mp[c][u],l,r);
int ct=al.first-sm.first;
LL len=al.second-sm.second+(ct)*(LL)dist[c][u];
ans+=len;
}
printf("%lld\n",ans);
}
return 0;
}