HHHOJ#225. 树的统计

树上莫队

题目大意:给你一棵树,每个节点有一个颜色,每次询问从 u u v出现第 k k 多的颜色。
数据范围:n,q1e5

这道题是LOJ6273的树上版。

对于每一种颜色,定义一个二元组 c,x c , x 表示颜色 c c 出现了x次,用vector存储,把它放到序列上并分块。记一个 f[] f [ ] 表示这种情况是否出现,每次修改颜色的时候把 a[c][x] a [ c ] [ x ] 去掉,把 a[c][x+1] a [ c ] [ x + 1 ] a[c][x1] a [ c ] [ x − 1 ] 加上。处理询问的时候做一个类似前缀和的东西就好了。

具体实现见代码:

#include<cmath>
#include<vector>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define F inline
#define V F void
using namespace std;
struct edge{ int next,to; }ed[N<<1];
struct query{ int x,y,id,t; }q[N];
struct node{ int x,s; }nd[N];
int n,k,Q,tp,B,d,LG,c[N],t[N],cc[N],p[N],ans[N],ti[N];
int h[N],fa[N][20],dep[N],stk[N],num[N],sum[N],id[N];
bool f[N],f1[N]; vector <int> a[N];
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
V writec(int x){ if (x>9) writec(x/10); putchar(x%10+48); }
V _write(int x){ writec(x),putchar('\n'); }
V addedge(int x,int y){ ed[++k]=(edge){h[x],y},h[x]=k; }
F bool cmp1(node a,node b){ return a.s==b.s?a.x>b.x:a.s>b.s; }
F bool cmp2(query a,query b){ return num[a.x]==num[b.x]?num[a.y]<num[b.y]:num[a.x]<num[b.x]; }
F int LCA(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    for (int j=LG;~j;j--)
        if (dep[fa[x][j]]>=dep[y]) x=fa[x][j];
    if (x==y) return x;
    for (int j=LG;~j;j--)
        if (fa[x][j]!=fa[y][j])
            x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
void dfs(int x){
    ti[x]=++ti[0]; int now=tp;
    for (int i=h[x],v;i;i=ed[i].next)
        if ((v=ed[i].to)!=fa[x][0]){
            dep[v]=dep[x]+1,fa[v][0]=x,dfs(v);
            if (tp-now>=B) for (d++;tp!=now;) num[stk[tp--]]=d;
        }
    stk[++tp]=x;
}
V nsrt(int x,int w){
    int l=a[x][sum[x]],r=a[x][sum[x]+=w];
    if (l) f1[l]=false,t[(l-1)/B+1]--;
    if (r) f1[r]=true,t[(r-1)/B+1]++;
}
V rvrs(int x){ nsrt(p[x],((f[x]^=1)<<1)-1); }
V mdfy(int x,int y){
    while (x!=y)
        if (dep[x]>dep[y]) rvrs(x),x=fa[x][0];
        else rvrs(y),y=fa[y][0];
}
F int srch(int x){
    int now=1;
    while (x>t[now]) x-=t[now++];
    for (int i=(now-1)*B+1;i<=now*B;i++)
        if (!(x-=f1[i])) return i;
}
int main(){
    n=_read(),Q=_read(),LG=log2(n),B=sqrt(n);
    for (int i=1;i<=n;i++) c[i]=cc[i]=_read();
    for (int i=1,x,y;i<n;i++)
        x=_read(),y=_read(),addedge(x,y),addedge(y,x);
    sort(cc+1,cc+n+1); int s=unique(cc+1,cc+n+1)-(cc+1);
    for (int i=1;i<=n;i++){
        c[i]=lower_bound(cc+1,cc+s+1,c[i])-cc;
        p[i]=nd[i].x=c[i],nd[i].s=++sum[c[i]];
    }
    sort(nd+1,nd+n+1,cmp1),dep[1]=1,dfs(1);
    for (int i=1;i<=s;i++) a[i].push_back(0);
    for (int i=n;i;i--)
        a[nd[i].x].push_back(i),id[i]=nd[i].x;
    for (int j=1;j<=LG;j++)
        for (int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    memset(sum,0,sizeof(sum));
    for (int i=1,x,y,k;i<=Q;i++){
        x=_read(),y=_read(),k=_read();
        if (ti[x]>ti[y]) swap(x,y);
        q[i]=(query){x,y,i,k};
    }
    sort(q+1,q+Q+1,cmp2);
    for (int i=1,x,y,l;i<=Q;i++){
        x=q[i].x,y=q[i].y,l=LCA(x,y);
        if (i==1) mdfy(x,y);
        else mdfy(q[i-1].x,x),mdfy(q[i-1].y,y);
        rvrs(l),ans[q[i].id]=id[srch(q[i].t)],rvrs(l);
    }
    for (int i=1;i<=Q;i++) _write(cc[ans[i]]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值