【HNOI2016模拟4.13】a

Description

给出一棵n个节点的树,每个节点ai的范围在1~m,和q次询问,每次询问x到y的路径中小于/等于/大于k的数的个数。强制在线。
n,q<=300000,m<=150000

Solution

裸题,用来练模板。
有两种做法,一种是主席树用lca直接搞。
一种是链剖开m棵线段树暴力搞。
这里打的是第一种。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(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 270000
using namespace std;
struct note{int sum,l,r;}tr[N*30];
int n,m,q,l,tot,root[N],a[N],d[N],f[N][20],ansl,ans,ansr,x,y,z;
int t[N*2],next[N*2],last[N];
void add(int x,int y) {
    t[++l]=y;next[l]=last[x];last[x]=l;
}
void insert(int &v,int l,int r,int x) {
    tr[++tot]=tr[v];tr[tot].sum++;v=tot;
    if (l==r) return;
    int m=(l+r)/2;
    if (x<=m) insert(tr[v].l,l,m,x);
    else insert(tr[v].r,m+1,r,x);
}
void dfs(int x,int y) {
    root[x]=root[y];f[x][0]=y;d[x]=d[y]+1;
    insert(root[x],1,m,a[x]);
    rep(i,x) if (t[i]!=y) dfs(t[i],x);
}
int lca(int x,int y) {
    if (d[x]<d[y]) swap(x,y);
    fd(j,19,0) if (d[f[x][j]]>d[y]) x=f[x][j];
    if (d[x]!=d[y]) x=f[x][0];
    fd(j,19,0) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
    if (x!=y) return f[x][0];else return x;
}
int find(int v,int l,int r,int x,int y) {
    if (!v) return 0;
    if (l==x&&r==y) return tr[v].sum;
    int m=(l+r)/2;
    if (y<=m) return find(tr[v].l,l,m,x,y);
    else if (x>m) return find(tr[v].r,m+1,r,x,y);
    else return find(tr[v].l,l,m,x,m)+find(tr[v].r,m+1,r,m+1,y);
}
int query(int x,int y,int l,int r) {
    if (l>r) return 0;
    int z=lca(x,y);
    return find(root[x],1,m,l,r)+find(root[y],1,m,l,r)-
    find(root[z],1,m,l,r)-find(root[f[z][0]],1,m,l,r);
}
int main() {
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n-1) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,0);
    fo(j,1,19) fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
    for(;q;q--) {
        scanf("%d%d%d",&x,&y,&z);int lastans=ansl^ans^ansr;
        x^=lastans;y^=lastans;z^=lastans;
        ansl=query(x,y,1,z-1);ans=query(x,y,z,z);ansr=query(x,y,z+1,m);
        printf("%d %d %d\n",ansl,ans,ansr);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值