[NOISG 2022 Qualification] Dragonfly 解题报告

[NOISG 2022 Qualification] Dragonfly 解题报告

简要题意

一棵有 \(n\) 个节点的树,每个节点有一个颜色 \(s_i\) 和权值 \(b_i\)。现在有 \(d\) 个形如 \(h_i\) 的询问,对于每个询问,你需要回答简单路径 \((1,h_i)\) 上权值不为 \(0\) 点的颜色种类;询问后将 \((1,h_i)\) 上所有点权不为 \(0\) 的点的点权减一。

数据范围:\(n \le 2\times 10^5\)\(d\le 2 \times 10^6\)

分析

考虑到 \(n\)\(d\) 的存在一定差距,因此从点的角度考虑。

因为我们点权是随询问递减的,所以一个点只能对一段区间的询问产生贡献。

因此考虑维护 \(lst_i\) 表示 \(i\) 号节点可以做出贡献的最大询问编号。注意到这东西具有单调性,因此考虑二分。对每一个点,二分已经进行的询问数。如果其子树内已经进行的询问次数大于 \(b_i\),那么返回 false;否则返回 true 。但是这东西是 \(O(nd\log d)\) 的( \(n\) 是枚举点,\(d\log d\) 是单次二分)。

注意到瓶颈是每个点二分都要重复插入询问,因此考虑整体二分,这样时间复杂度就优化到了 \(O(d\log^2 d)\) 的。

之后考虑怎么处理询问。注意到每个询问都是根到节点的路径,因此考虑维护当前每种颜色可以贡献的最大询问编号,并在树状数组上更新每个询问编号的答案。搜索到一个节点时,维护当前节点的询问即可。

总时间复杂度 \(O(dlog^2d)\)

正解

题解给出了更优的时间复杂度,因为一个节点子树内的所有询问编号可以通过线段树合并维护的,因此在合并出的线段树上跑线段树二分求第 \(b_i\) 大就可以了。

时间复杂度 \(O(dlogd)\)

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define ls (i<<1)
#define rs (i<<1|1)
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
    register int x=0,f=1;
    char c=getchar();
    while(c<'0' || '9'<c) f=(c=='-')?-1:1,c=getchar();
    while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
    return x*f;
}
void write(int x){
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int N=2e6+100,M=2e6+100,P=2e6;
int n,m,a[N],s[N],lst[N],p[M],ans[M],c[M];
struct Edge{int from,to;}e[N<<1];
int num,h[N];
void add_edge(int f,int t){e[++num].from=h[f],e[num].to=t,h[f]=num;}
int dfn[N],sz[N],dfu;
void dfs(int u,int fa){
    dfn[u]=++dfu,sz[u]=1;
    for(int i=h[u],v;i;i=e[i].from){
        v=e[i].to;
        if(v!=fa)
            dfs(v,u),sz[u]+=sz[v];
    }
}
int id[N],idl[N],idr[N];
inline void add(int x,int k){
    for(;x && x<=P;x+=lowbit(x))
        c[x]+=k;
}
inline int ask(int x){
    int ans=0;
    for(;x;x-=lowbit(x))
        ans+=c[x];
    return ans;
}
//二分区间 [l,r] 询问下标[x,y]
void solve(int l,int r,int x,int y){
    if(l==r){
        For(i,x,y) lst[id[i]]=l;
        return;
    }
    //printf("In [%d,%d] [%d,%d]:\n",l,r,x,y);
    int mid=l+r>>1,res,cntl=0,cntr=0;
    For(i,l,mid) add(dfn[p[i]],1);
    For(i,x,y){
        res=ask(dfn[id[i]]+sz[id[i]]-1)-ask(dfn[id[i]]-1);
        //printf("%d gain %d\n",id[i],res);
        if(res>=a[id[i]])
            idl[++cntl]=id[i];
        else
            idr[++cntr]=id[i],a[id[i]]-=res;
    }
    For(i,l,mid) add(dfn[p[i]],-1);
    For(i,1,cntl) id[x+i-1]=idl[i];//,printf("%d ",idl[i]);
    //putchar('\n');
    For(i,1,cntr) id[x+cntl+i-1]=idr[i];//,printf("%d ",idr[i]);
    //putchar('\n');
    solve(l,mid,x,x+cntl-1);
    solve(mid+1,r,x+cntl,y);
}
int range[N],hd[N],ask_id[M],nxt[M],ask_num;
void add_quest(int x,int p){nxt[++ask_num]=hd[p],ask_id[ask_num]=x,hd[p]=ask_num;}
void dfs1(int u,int fa){
    int old=-1;
    if(lst[u]>range[s[u]]){
        old=range[s[u]];
        if(old) add(m-old+1,-1);
        range[s[u]]=lst[u];
        add(m-lst[u]+1,1);
        //printf("color %d update to %d at point %d\n",s[u],lst[u],u);
    }
    for(int i=hd[u];i;i=nxt[i]){
        ans[ask_id[i]]=ask(m-ask_id[i]+1);
        //printf("%d ask and get %d at point %d\n",ask_id[i],ans[ask_id[i]],u);
    }
    for(int i=h[u];i;i=e[i].from)
        if(e[i].to!=fa)
            dfs1(e[i].to,u);
    if(old!=-1){
        add(m-lst[u]+1,-1);
        range[s[u]]=old;
        if(old) add(m-old+1,1);
        //printf("color %d update to %d at point %d\n",s[u],old,u);
    }
}
int main()
{
    //freopen("woodpecker.in","r",stdin);
    //freopen("woodpecker.out","w",stdout);
    n=read(),m=read();
    For(i,1,n) a[i]=read();
    For(i,1,n) s[i]=read();
    For(i,1,m) p[i]=read();
    int u,v;
    For(i,2,n) u=read(),v=read(),add_edge(u,v),add_edge(v,u);
    dfs(1,0);
    For(i,1,n) id[i]=i;
    solve(0,m,1,n);
    memset(c,0,sizeof(c));
    //For(i,1,n) printf("lst[%d]=%d\n",i,lst[i]);
    For(i,1,m) add_quest(i,p[i]);
    dfs1(1,0);
    For(i,1,m)
        printf("%d ",ans[i]);
    return 0;
}
posted @ 2025-07-14 17:42  XiaoZi_qwq  阅读(18)  评论(0)    收藏  举报