[HNOI2015]开店

[HNOI2015]开店 

法一

一个点到所有点距离?

树上路径问题,从一个点出发的

虽然不是统计一次,虽然强制在线,点分治也可以做!

因为可以动态点分治的分治树来搞!

vector维护到根路径dis,按age sort之后,处理后缀和

再维护到分治树father的贡献

O(nlog^2n)

代码:

#pragma GCC optimize("O2,Ofast,inline,unroll-all-loops,-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,popcnt")
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=150000+5;
const int inf=0x3f3f3f3f;
int n,q,A;
int age[N];
struct node{
    int nxt,to,val;
}e[2*N];
int hd[N],cnt;
void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;e[cnt].val=z;hd[x]=cnt;
}
int sz[N],nowsz;
struct po{
    int id,dis;
    ll bac;
    po(){}
    po(int dd,int ds){
        id=dd;dis=ds;bac=ds;
    }
    bool friend operator <(po a,po b){
        if(a.id!=b.id) return a.id<b.id;
        return a.dis<b.dis;
    }
    void op(){
        cout<<" id "<<id<<" dis "<<dis<<" bac "<<bac<<endl;
    }
};
vector<po>mem[N];
int dis[N][19];
int fa[N];
vector<po>df[N];
int rt;//,sz[N];
int vis[N];
int dep[N];
void fin(int x,int fa){
    sz[x]=1;
    int mxsz=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa||vis[y]) continue;
        fin(y,x);
        sz[x]+=sz[y];
        mxsz=max(mxsz,sz[y]);
    }
    mxsz=max(mxsz,nowsz-sz[x]);
    if(mxsz<=nowsz/2){
        rt=x;
    }
}
void dfs2(int x,int fa,int d){
    sz[x]=1;
    if(d!=1) df[rt].push_back(po(age[x],dis[x][d-1]));
    mem[rt].push_back(po(age[x],dis[x][d]));
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa||vis[y]) continue;
        dis[y][d]=dis[x][d]+e[i].val;
        dfs2(y,x,d);
        sz[x]+=sz[y];
    }
}
void divi(int x,int d,int f){
//    cout<<" divi "<<x<<" d "<<d<<" f "<<f<<" nowsz "<<nowsz<<endl;
    rt=0;
    fin(x,0);
//    cout<<" rt "<<rt<<endl;
    dep[rt]=d;
    fa[rt]=f;
    dfs2(rt,0,d);
    if(d!=1){
        sort(df[rt].begin(),df[rt].end());
        for(reg i=df[rt].size()-2;i>=0;--i){
            df[rt][i].bac=df[rt][i+1].bac+df[rt][i].dis;
        }
    }
    sort(mem[rt].begin(),mem[rt].end());
    for(reg i=mem[rt].size()-2;i>=0;--i){
        mem[rt][i].bac=mem[rt][i+1].bac+mem[rt][i].dis;
    }
    vis[rt]=1;
    int tmp=rt;
    for(reg i=hd[tmp];i;i=e[i].nxt){
        int y=e[i].to;
        if(!vis[y]){
            nowsz=sz[y];
            divi(y,d+1,tmp);
        }
    }
}
ll wrk(int x,int L,int R){
    ll ret=0;
    int le=lower_bound(mem[x].begin(),mem[x].end(),po(L,-1))-mem[x].begin();
    int ri=upper_bound(mem[x].begin(),mem[x].end(),po(R,inf))-mem[x].begin();
    if(le!=mem[x].size()) ret+=mem[x][le].bac;
    if(ri!=mem[x].size()) ret-=mem[x][ri].bac;
//    cout<<"start "<<ret<<endl;
    int st=x;
    while(fa[x]){
        int y=fa[x];
//        cout<<" yy "<<y<<" dis "<<dis[st][dep[y]]<<endl;
//        for(reg i=0;i<mem[y].size();++i){
//            cout<<" i "<<i+1;mem[y][i].op();cout<<endl;
//        }
        int le=lower_bound(mem[y].begin(),mem[y].end(),po(L,-1))-mem[y].begin();
        int ri=upper_bound(mem[y].begin(),mem[y].end(),po(R,inf))-mem[y].begin();
//        cout<<" le ri "<<le<<" "<<ri<<endl;
        if(le!=mem[y].size()) ret+=mem[y][le].bac,ret+=(ll)(mem[y].size()-le)*dis[st][dep[y]];
        if(ri!=mem[y].size()) ret-=mem[y][ri].bac,ret-=(ll)(mem[y].size()-ri)*dis[st][dep[y]];
//        cout<<" ret before dele "<<ret<<endl;
        le=lower_bound(df[x].begin(),df[x].end(),po(L,-1))-df[x].begin();
        ri=upper_bound(df[x].begin(),df[x].end(),po(R,inf))-df[x].begin();
        if(le!=df[x].size()) ret-=df[x][le].bac,ret-=(ll)(df[x].size()-le)*dis[st][dep[y]];
        if(ri!=df[x].size()) ret+=df[x][ri].bac,ret+=(ll)(df[x].size()-ri)*dis[st][dep[y]];        
        x=fa[x];
    }
    return ret;
}
int main(){
    rd(n);rd(q);rd(A);
    for(reg i=1;i<=n;++i) rd(age[i]);
    int x,y,z;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);rd(z);add(x,y,z);add(y,x,z);
    }
    nowsz=n;
    divi(1,1,0);
    ll ans=0;
    int a,b,L,R;
    while(q--){
        rd(x);rd(a);rd(b);
        L=min((a+ans)%A,(b+ans)%A),R=max((a+ans)%A,(b+ans)%A);
//        cout<<" true "<<x<<" "<<L<<" "<<R<<endl;
        printf("%lld\n",ans=wrk(x,L,R));
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/3/23 19:57:03
*/
View Code

 

 

法二:

两个点距离怎么算?

距离差分?

考虑距离是怎么算的,∑dis+n*dis(x)-2*dis(lca)

前面两个好算

考虑每个点贡献作为lca应有的贡献,也就是经过的sz

暴力跳不行,考虑树剖!

考虑x头上的边贡献

无[L,R]限制,直接把每个点到根的链上的sz++,

有[L,R],主席树rt按照age,主席树的叶子下标是dfn序,权值是[1,i]颜色的fdfn[p]的子树的个数和

加入一个点,重链跳,主席树标记永久化区间加

查询时候,找一段链作为lca的贡献,就是主席树差分查区间和

注意减去u子树的贡献

 

 

一个点有时候携带多个信息

一个区间的点的信息查询可以用主席树

树链的点的信息查询,树剖+主席树!

posted @ 2019-03-23 21:34  *Miracle*  阅读(105)  评论(0编辑  收藏  举报