NKOJ 4345 (Ipsc2015)Generating Synergy (DFS序+kd树)

P4345[Ipsc2015]Generating Synergy

问题描述

给定一棵以1为根的有根树,初始所有节点颜色为1,每次将距离节点a不超过l的a的子节点染成c,或询问点a的颜色

输入格式

第一行一个数T,表示数据组数

接下来每组数据的第一行三个数n,c,q表示结点个数,颜色数和操作数

接下来一行n-1个数描述2..n的父节点

接下来q行每行三个数a,l,c

若c为0,表示询问a的颜色

否则将距离a不超过l的a的子节点染成c

输出格式

设当前是第i个操作,y_i为本次询问的答案(若本次操作是一个修改则y_i为0),令z_i=i*y_i,请输出z_1+z_2+…+z_q模10^9+7

样例输入

1
4 3 7
1 2 2
3 0 0
2 1 3
3 0 0
1 0 2
2 0 0
4 1 1
4 0 0

样例输出

32

提示

第1,3,5,7的询问的答案分别为1,3,3,1,所以答案为 1*1+2*0+3*3+4*0+5*3+6*0+7*1=32.

数据范围:

对于100%的数据T<=6,n,m,c<=10^5,

1<=a<=n,0<=l<=n,0<=c<=c


首先求出这棵树的DFS序和每个节点的深度,那么将DFS序的 In 作为 x 坐标,将深度作为y坐标,建立kd树,那么每次询问直接找对应点,修改就变成将以 (In[a],dep[a]) (Out[a],dep[a]+l) 为左上和右下顶点的矩形区域整体修改,kd树+lazy即可解决。

另外,也可以用树套树维护,但显然不如kd树好写。


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 222222
#define ll long long
using namespace std;
int rt,D,T,n,c,q,x1,x2,y1,y2;
int L[N],R[N],dep[N],VT,mod=1e9+7;
int LA[N],NE[N],EN[N],TOT;
struct node
{
    int d[2],x[2],y[2],son[2],v,lazy;
    bool operator<(const node &b)const
    {return d[D]<b.d[D];}
}kdt[N];
void PD(int p)
{
    int ls=kdt[p].son[0],rs=kdt[p].son[1];
    kdt[ls].v=kdt[rs].v=kdt[p].lazy;
    kdt[ls].lazy=kdt[rs].lazy=kdt[p].lazy;
    kdt[p].lazy=0;
}
void MT(int p,int s)
{
    kdt[p].x[0]=min(kdt[p].x[0],kdt[s].x[0]);
    kdt[p].x[1]=max(kdt[p].x[1],kdt[s].x[1]);
    kdt[p].y[0]=min(kdt[p].y[0],kdt[s].y[0]);
    kdt[p].y[1]=max(kdt[p].y[1],kdt[s].y[1]);
}
int BT(int l,int r,int d)
{
    D=d;int o=l+r>>1;
    kdt[o].v=1;
    kdt[o].x[0]=kdt[o].x[1]=kdt[o].d[0];
    kdt[o].y[0]=kdt[o].y[1]=kdt[o].d[1];
    if(l<o)kdt[o].son[0]=BT(l,o-1,d^1),MT(o,kdt[o].son[0]);
    if(o<r)kdt[o].son[1]=BT(o+1,r,d^1),MT(o,kdt[o].son[1]);
    return o;
}
void ADD(int x,int y)
{
    TOT++;
    EN[TOT]=y;
    NE[TOT]=LA[x];
    LA[x]=TOT;
}
void DFS(int x,int f)
{
    L[x]=++VT;dep[x]=dep[f]+1;
    for(int i=LA[x];i;i=NE[i])DFS(EN[i],x);
    R[x]=VT;
}
int GA(int p)
{
    if(kdt[p].lazy)PD(p);
    if(kdt[p].d[0]==x1&&kdt[p].d[1]==y1)return kdt[p].v;
    int s=0,ls=kdt[p].son[0],rs=kdt[p].son[1];
    if(kdt[ls].x[0]<=x1&&kdt[ls].x[1]>=x1&&kdt[ls].y[0]<=y1&&kdt[ls].y[1]>=y1)s+=GA(ls);
    if(kdt[rs].x[0]<=x1&&kdt[rs].x[1]>=x1&&kdt[rs].y[0]<=y1&&kdt[rs].y[1]>=y1)s+=GA(rs);
    return s;
}
void CHA(int p,int k)
{
    if(kdt[p].lazy)PD(p);
    if(kdt[p].d[0]<=x2&kdt[p].d[0]>=x1&&kdt[p].d[1]<=y2&&kdt[p].d[0]>=y1)kdt[p].v=k;
    if(kdt[p].x[0]>x2||kdt[p].x[1]<x1||kdt[p].y[0]>y2||kdt[p].y[1]<y1)return;
    if(kdt[p].x[1]<=x2&&kdt[p].x[0]>=x1&&kdt[p].y[1]<=y2&&kdt[p].y[0]>=y1){kdt[p].v=kdt[p].lazy=k;return;}
    CHA(kdt[p].son[0],k);CHA(kdt[p].son[1],k);
}
int main()
{
    int i,k,x,y;ll ans;
    scanf("%d",&T);
    while(T--)
    {
        TOT=VT=ans=0;
        memset(kdt,0,sizeof(kdt));
        memset(LA,0,sizeof(LA));
        scanf("%d%d%d",&n,&c,&q);
        for(i=2;i<=n;i++)scanf("%d",&x),ADD(x,i);
        DFS(1,0);
        for(i=1;i<=n;i++)kdt[i].d[0]=L[i],kdt[i].d[1]=dep[i];
        rt=BT(1,n,0);
        for(i=1;i<=q;i++)
        {
            scanf("%d%d%d",&x,&y,&k);
            if(k==0)
            {
                x1=L[x];y1=dep[x];
                ans=(ans+1ll*GA(rt)*i)%mod;
            }
            else
            {
                x1=L[x];x2=R[x];
                y1=dep[x];y2=dep[x]+y;
                CHA(rt,k);
            }
        }
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值