JZOJ 5699. 【gdoi2018 day1】涛涛接苹果(appletree)

本文介绍了一种利用树状数组与CDQ分治解决特定树形结构上动态查询问题的方法。该方法适用于数字随时间在树上移动并进行子树区间查询的场景,通过对数字位置和时间的转换,采用树状数组进行高效查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Description
Description

Input

Input

Output

Output

Sample Input

10 5 6
1 2 3 4 5 6 7 8 9 10
9 7
7 10
6 5
7 5
5 8
5 1
2 1
3 2
2 4
2 3 4
2 9 5
1 7 3
4 8 2
5 6 6
2 3
2 5
1 4
3 5
5 1
6 1

Sample Output

0
43
4
27
11
13

Data Constraint

Data Constraint

Hint

Hint

Solution

题意:

  • 给定一棵以 1 为根的树,树上结点上有一些数字。

  • 这些数字每过单位 1 时间会走到其所在结点的父亲上,结点 1 上的数字会消失。

  • 有若干询问,问某一时间一棵子树中的数字和。

思路:

  • 我们考虑一个数字会被一个询问统计到是一种什么情况:

  • 设这个数字在 txtx 时间出现在结点 xx 上,然后会被结点 ytyty 时间统计,

  • 那么就会满足不等式:

    dep[x]dep[y]tytxdep[x]−dep[y]≥ty−txtxtytx≤ty
    xx 必须在 y 的子树内;

  • 其中 dep[i]dep[i] 表示结点 ii 的深度。

  • 考虑①:我们移项,就会有 dep[x]+txdep[y]+ty

  • 显然左边只会和 xx 有关,右边只会和 y 有关;

  • 考虑②:我们考虑使用 dfsdfs 序,

  • 那么②等价于 dfn[x]dfn[y]dfn[x]≥dfn[y]dfn[x]<dfn[x]+size[x]dfn[x]<dfn[x]+size[x]

  • 其中 dfn[i]dfn[i] 表示 iidfs 序,

  • Size[i]Size[i] 表示以 ii 为子树的结点个数。

  • 综上,我们把题目中的第 x 数字写成二维平面上的一个带权点,

  • 横坐标为 dep[x]+txdep[x]+tx ,纵坐标为 dfn[x]dfn[x],权值为这个数字。

  • 那么询问就相当于是询问一个矩形的点权和。

  • 为了支持平面上单点修改,矩形求和。

  • 我们有两种方法:

    1 . cdq分治+树状数组

  • 我们把修改和询问在一起按时间(题目中的 tt)排序,一共 n+m+q 个东西。

  • 我们从 [1,n+m+q][1,n+m+q] 开始递归;

  • 对于递归一个 [l,r][l,r] 的区间,设 mid=(l+r)/2mid=(l+r)/2

  • 我们只考虑 [l,mid][l,mid] 的修改对 [mid+1,r][mid+1,r] 的询问的贡献;

  • 这之后,递归到 [l,mid][l,mid] , [mid+1,r][mid+1,r] 中继续处理。

  • 现在我们就只用考虑 [l,mid][l,mid] 的修改对 [mid+1,r][mid+1,r] 的询问的贡献;

  • 我们可以简单地对 [l,mid][l,mid] 中的修改的横坐标排序,然后把纵坐标插入树状数组中,

  • 那么 [mid+1,r][mid+1,r] 中的询问就能通过查询树状数组来实现统计的过程。

    2 . 二维数据结构

  • 这个就直接做就好了。

  • 只要你会任何二维的数据结构就可以。

  • 推荐学习:树状数组套线段树

  • 时间复杂度 O(Nlog2N)O(Nlog2N)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=1e5+5;
struct data
{
    int ty,t,x,w;
}a[N*3];
int tot,num;
int first[N],nex[N<<1],en[N<<1];
int dep[N],dfn[N],size[N];
int id[N*3];
long long f[N],ans[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline void insert(int x,int y)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
void dfs(int x,int y)
{
    dfn[x]=++tot;
    dep[x]=dep[y]+1;
    size[x]=1;
    for(int i=first[x];i;i=nex[i])
        if(en[i]^y)
        {
            dfs(en[i],x);
            size[x]+=size[en[i]];
        }
}
inline bool cmpt(data x,data y)
{
    return x.t<y.t || x.t==y.t && !x.ty && y.ty;
}
inline bool cmpx(int x,int y)
{
    return a[x].t>a[y].t;
}
inline void change(int x,int y)
{
    while(x<N) f[x]=y?f[x]+y:0,x+=x&-x;
}
inline long long find(int x) 
{
    long long sum=0;
    while(x) sum+=f[x],x-=x&-x;
    return sum;
}
void solve(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    int nn=l-1,mm=mid;
    for(int i=l;i<=mid;i++)
        if(!a[i].ty) id[++nn]=i;
    for(int i=mid+1;i<=r;i++)
        if(a[i].ty) id[++mm]=i;
    if(nn>=l && mm>mid)
    {
        sort(id+l,id+1+nn,cmpx);
        sort(id+mid+1,id+1+mm,cmpx);
        for(int i=mid+1,j=l;i<=mm;i++)
        {
            while(j<=nn && a[id[j]].t>=a[id[i]].t)
            {
                change(dfn[a[id[j]].x],a[id[j]].w);
                j++;
            }
            ans[a[id[i]].w]+=find(dfn[a[id[i]].x]+size[a[id[i]].x]-1)-find(dfn[a[id[i]].x]-1);
        }
        for(int i=l;i<=nn;i++) change(dfn[a[id[i]].x],0);
    }
    if(nn>=l && nn<mid) solve(l,mid);
    if(mm>mid && mm<r) solve(mid+1,r);
}
int main()
{
    freopen("appletree.in","r",stdin);
    freopen("appletree.out","w",stdout);
    int n=read(),m=read(),q=read();
    for(int i=1;i<=n;i++)
    {
        a[++num].x=i;
        a[num].t=1;
        a[num].w=read();
    }
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    dfs(1,tot=0);
    for(int i=1;i<=m;i++)
    {
        a[++num].t=read()+1;
        a[num].x=read();
        a[num].w=read();
    }
    for(int i=1;i<=q;i++)
    {
        a[++num].ty=1;
        a[num].t=read();
        a[num].x=read();
        a[num].w=i;
    }
    sort(a+1,a+1+num,cmpt);
    for(int i=1;i<=num;i++) a[i].t+=dep[a[i].x];
    solve(1,num);
    for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值