[bzoj3626][LNOI2014]LCA

Description

给出一棵树,q次询问,每次询问

i=lrdeep(lca(i,z))

n,q<=5*10^4

Solution

这道题在线明显不可做。(在线A了的大犇请收下我的膝盖)
首先,我们来思考一下,一组询问怎么做?
暴力的想法,我们把每要询问的点往上都打上标记,然后把z点往上找到每个标记第一次出现的位置。
其实我们可以发现,每个标记的深度等同于它上面有多少个和它一样的标记。
那么我们可以把每个点到根的路径都+1,然后询问z到根的和,就是答案了。
很显然可以用链剖搞。(两个log不优美?原谅本蒟蒻不会打LCT)
那么多组询问呢?
本来我是想到用莫队算法的,但是复杂度过于爆炸,多出来的根号n实在碍眼。
换种思路,区间[l,r]的答案=区间[1,r]的答案-区间[1,l-1]的答案。
那么我们把每个询问拆成两个,一个+,一个-。
把询问按端点排序,依次往后推,就可以满足线性了。
Orz 全民偶像hzwer

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 50005
using namespace std;
const int mo=201314;
struct note{int p,z,id,bz;}ask[N*2];
int t[N],next[N],last[N],size[N],son[N],w[N],top[N],fa[N];
int n,m,tot,l,r,tr[N*3],add[N*3],an[N*2];
bool cmp(note x,note y) {return x.p<y.p;}
void ins(int x,int y) {
    t[++l]=y;next[l]=last[x];last[x]=l;
}
void dfs(int x) {
    size[x]=1;int k=0;
    rep(i,x) {
        dfs(t[i]);size[x]+=size[t[i]];
        if (size[t[i]]>k) k=size[t[i]],son[x]=t[i];
    }
}
void make(int x,int y) {
    w[x]=++tot;top[x]=y;
    if (!son[x]) return;
    make(son[x],y);
    rep(i,x) if (t[i]!=son[x]) make(t[i],t[i]);
}
void back(int v,int x,int len) {
    (tr[v]+=len*x)%=mo;add[v]+=x;
}
void down(int v,int l,int r) {
    if (add[v]) {
        int m=(l+r)/2;
        back(v*2,add[v],m-l+1);
        back(v*2+1,add[v],r-m);
        add[v]=0;
    }
}
void change(int v,int l,int r,int x,int y) {
    if (l==x&&r==y) {back(v,1,r-l+1);return;}
    int m=(l+r)/2;down(v,l,r);
    if (y<=m) change(v*2,l,m,x,y);
    else if (x>m) change(v*2+1,m+1,r,x,y);
    else change(v*2,l,m,x,m),change(v*2+1,m+1,r,m+1,y);
    (tr[v]=tr[v*2]+tr[v*2+1])%=mo;
} 
int find(int v,int l,int r,int x,int y) {
    if (l==x&&r==y) return tr[v];
    int m=(l+r)/2;down(v,l,r);
    if (y<=m) return find(v*2,l,m,x,y);
    else if (x>m) return find(v*2+1,m+1,r,x,y);
    else return find(v*2,l,m,x,m)+find(v*2+1,m+1,r,m+1,y);
}
void revise(int x) {
    int f=top[x];
    while (x) {
        change(1,1,n,w[f],w[x]);
        x=fa[f];f=top[x];
    }
}
int query(int x) {
    int f=top[x],ans=0;
    while (x) {
        (ans+=find(1,1,n,w[f],w[x]))%=mo;
        x=fa[f];f=top[x];
    }
    return ans;
}
int main() {
    scanf("%d%d",&n,&m);
    fo(i,2,n) scanf("%d",&fa[i]),ins(++fa[i],i);
    dfs(1);make(1,1);tot=0;
    fo(i,1,m) {
        scanf("%d%d%d",&l,&r,&ask[++tot].z);l++,r++,ask[tot].z++;
        ask[tot].p=l-1;ask[tot].id=i;ask[tot].bz=-1;
        ask[++tot].p=r;ask[tot].id=i;ask[tot].bz=1;ask[tot].z=ask[tot-1].z;
    }
    sort(ask+1,ask+tot+1,cmp);int j=0;while (!ask[j+1].p) j++;
    fo(i,1,n) {
        revise(i);
        while (ask[j+1].p==i) j++,an[ask[j].id]+=query(ask[j].z)*ask[j].bz;
    }
    fo(i,1,m) printf("%d\n",(an[i]+mo)%mo);
}

你可能感兴趣的:(LCA,离线,树链剖分,BZOJ3626,LNOI2014)