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

5 篇文章 0 订阅
3 篇文章 0 订阅
Time Limits: 2000 ms Memory Limits: 513000 KB

Description

这里写图片描述
这里写图片描述

Input

这里写图片描述

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

这里写图片描述

Hint

这里写图片描述

Solution

题意:

给定一棵以 1 为根的树,树上结点上有一些数字。
这些数字每过单位 1 时间会走到其所在结点的父亲上,结点 1 上的数字会消失。
有若干询问,问某一时间一棵子树中的数字和。

思路:

我们考虑一个数字会被一个询问统计到是一种什么情况:
设这个数字在 tx t x 时间出现在结点 x x 上,然后会被结点 y ty t y 时间统计
那么就会满足不等式:
dep[x] d e p [ x ] - dep[y] d e p [ y ] >= tytx t y − t x tx<=ty t x <= t y
x x 必须是 y 的儿子;
其中 dep[i] d e p [ i ] 表示结点 i i 的深度。
考虑①:
我们移项,就会有 dep[x]+tx>= dep[y]+ty d e p [ y ] + t y
显然左边只会和 x x 有关,右边只会和 y 有关;
考虑②:
我们考虑使用 dfs d f s 序,
那么②等价于 dfn[x]>=dfn[y] d f n [ x ] >= d f n [ y ] dfn[x]<dfn[x]+size[x] d f n [ x ] < d f n [ x ] + s i z e [ x ]
其中 dfn[i] d f n [ i ] 表示 i i dfs 序, size[i] s i z e [ i ] 表示以 i i 为子树的结点个数。
综上,我们把题目中的第 x 数字写成二维平面上的一个带权点,
横坐标为 dep[x]+tx d e p [ x ] + t x
纵坐标为 dfn[x] d f n [ x ]
权值为这个数字。
那么询问就相当于是询问一个矩形的点权和。
为了支持平面上单点修改,矩形求和。
我们有两种方法:
1. 分治+树状数组
我们把修改和询问在一起按时间(题目中的 t t )排序,一共 n+m+q 个东西。
我们从 [1,n+m+q] [ 1 , n + m + q ] 开始递归;
对于递归一个 [l,r] [ l , r ] 的区间,设 mid=(l+r)/2 m i d = ( l + r ) / 2
我们只考虑 [l,mid] [ l , m i d ] 的修改对 [mid+1,r] [ m i d + 1 , r ] 的询问的贡献;
这之后,递归到 [l,mid] [ l , m i d ] , [mid+1,r] [ m i d + 1 , r ] 中继续处理。
现在我们就只用考虑 [l,mid] [ l , m i d ] 的修改对 [mid+1,r] [ m i d + 1 , r ] 的询问的贡献;
我们可以简单地对 [l,mid] [ l , m i d ] 中的修改的横坐标排序,然后把纵坐标插入树状数组中,
那么 [mid+1,r] [ m i d + 1 , r ] 中的询问就能通过查询树状数组来实现统计的过程。
2. 二维数据结构
这个就直接做就好了。
只要你会任何二维的数据结构就可以。
推荐学习:树状数组套线段树。
时间复杂度 O(Nlog2N) O ( N l o g 2 N )

Code

#include<algorithm>
#include<cstdio>
#include<cctype>

#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define ll long long

using namespace std;

const int N=1e5+5;
int x,y,i,n,m,q,t,num,tot;
int w[N],last[N],id[3*N];
int dfn[N],dep[N],size[N];
ll f[N],ans[N];

struct edge
{
    int to,next;
}b[2*N];

struct node
{
    int t,x,w,id;
}op[3*N];

inline int read(int &n)
{
    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();
    n=w?-X:X;
}

inline void write(ll x)
{
    if(x<0) x=-x,putchar('-');
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

void link(int x,int y)
{
    b[++t].to=y,b[t].next=last[x],last[x]=t;
}

void dfs(int x,int y)
{
    dfn[x]=++num,dep[x]=dep[y]+1,size[x]=1;
    for(int w=last[x];w;w=b[w].next) if(b[w].to!=y)
        dfs(b[w].to,x),size[x]+=size[b[w].to];
}

bool cmp(node p,node q)
{
    return p.t<q.t||p.t==q.t&&!p.id&&q.id;
}

bool cmq(int x,int y)
{
    return op[x].t>op[y].t;
}

void modify(int x,int y)
{
    while(x<=n) f[x]=y?f[x]+y:0,x+=x&(-x);
}

ll find(int x)
{
    ll sum=0;
    while(x) sum+=f[x],x-=x&(-x);
    return sum;
}

void cdq(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    int u=l-1,v=mid;
    for(int i=l;i<=mid;i++) if(!op[i].id) id[++u]=i;
    for(int i=mid+1;i<=r;i++) if(op[i].id) id[++v]=i;
    if(u>=l&&v>mid)
    {
        sort(id+l,id+u+1,cmq);
        sort(id+mid+1,id+v+1,cmq);
        for(int i=l,j=mid+1;j<=v;j++)
        {
            while(op[id[i]].t>=op[id[j]].t&&i<=u) modify(dfn[op[id[i]].x],op[id[i]].w),i++;
            ans[op[id[j]].id]+=find(dfn[op[id[j]].x]+size[op[id[j]].x]-1)-find(dfn[op[id[j]].x]-1);
        }
        for(int i=l;i<=u;i++) modify(dfn[op[id[i]].x],0);
    }
    if(l<=u&&u<mid) cdq(l,mid);
    if(mid<v&&v<r) cdq(mid+1,r);
}

int main()
{
    freopen("appletree.in","r",stdin);
    freopen("appletree.out","w",stdout);
    read(n),read(m),read(q),tot=0;
    fo(i,1,n)
    {
        read(op[++tot].w);
        op[tot].x=i;
        op[tot].t=1;
    }
    fo(i,1,n-1)
    {
        read(x),read(y);
        link(x,y),link(y,x);
    }
    num=0,dfs(1,0);
    fo(i,1,m)
    {
        read(op[++tot].t),op[tot].t++;
        read(op[tot].x);
        read(op[tot].w);
    }
    fo(i,1,q)
    {
        read(op[++tot].t);
        read(op[tot].x);
        op[tot].id=i;
    }
    sort(op+1,op+tot+1,cmp);
    fo(i,1,tot) op[i].t+=dep[op[i].x];
    cdq(1,tot);
    fo(i,1,q) write(ans[i]),putchar('\n');
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值