BZOJ 3626 [LNOI2014]LCA

给出一个n个节点的有根树。有q次询问,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)。
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
树链剖分+BIT
PS:用BIT求区间和非常赞

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50005;

void rd(int &r){
    r=0;char c;
    while(c=getchar(),c<48);
    do r=r*10+(c^48);
    while(c=getchar(),c>47);
}

int n,Q;

int last[maxn],ecnt;
struct Edge{
    int to,nxt;
}edge[maxn<<1];
void ins(int u,int v){
    edge[++ecnt]=(Edge){v,last[u]};
    last[u]=ecnt;
}

int frm[maxn];
int head[maxn],qcnt;
struct Query{
    int id,w,nxt;
}qury[maxn<<1];
void ins_(int id,int w,int to){
    qury[++qcnt]=(Query){id,w,head[to]};
    head[to]=qcnt;
}

int dfs_clock;
int segID[maxn];
int sz[maxn];
int link[maxn],par[maxn];

void dfs(int x){
    sz[x]=1;
    for(int i=last[x];i;i=edge[i].nxt){
        int to=edge[i].to;
        par[to]=x;
        dfs(to);
        sz[x]+=sz[to];
    }
}
void assign(int x,int f){
    segID[x]=++dfs_clock;
    link[x]=f;
    int mx=0;
    for(int i=last[x];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(sz[to]>sz[mx])
            mx=to;
    }
    if(!mx)return;
    assign(mx,f);
    for(int i=last[x];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(mx!=to)
            assign(to,to);
    }
}

struct SUM{
    struct BIT{
        int v[maxn];
        void update(int x,int f){
            for(;x<=n;x+=x&-x)v[x]+=f;
        }
        int query(int x){
            int res=0;
            for(;x;x-=x&-x)res+=v[x];
            return res;
        }
    }bit[2];
    void update(int l,int r,int x){
        bit[0].update(l,(1-l)*x);
        bit[0].update(r+1,r*x);
        bit[1].update(l,x);
        bit[1].update(r+1,-x);
    }
    int query(int l,int r){
        int rsum=bit[1].query(r)*r+bit[0].query(r);
        int lsum=bit[1].query(l-1)*(l-1)+bit[0].query(l-1);
        return rsum-lsum;
    }
}sum;

void update(int x){
    while(link[x]!=0){
        sum.update(segID[link[x]],segID[x],1);
        x=par[link[x]];
    }
}

int Ans[maxn];

void query(int y){
    int id=qury[y].id;
    int x=frm[id];
    int w=qury[y].w;
    while(link[x]!=0){
        Ans[id]+=w*sum.query(segID[link[x]],segID[x]);
        x=par[link[x]];
    }
}

void solve(){
    dfs(1);
    assign(1,1);
    for(int i=1;i<=n;i++){
        update(i);
        for(int j=head[i];j;j=qury[j].nxt)
            query(j);
    }
}

void input(){
    rd(n);rd(Q);
    for(int u,i=2;i<=n;i++){
        rd(u);++u;
        ins(u,i);
    }
    for(int l,r,i=1;i<=Q;i++){
        rd(l);rd(r);rd(frm[i]);
        ++l;++r;++frm[i];
        ins_(i,1,r);
        if(l!=1)ins_(i,-1,l-1);
    }
}

const int mod=201314;

void output(){
    for(int i=1;i<=Q;i++)
        printf("%d\n",Ans[i]%mod);
}
int main(){

    input();
    solve();
    output();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值