[Ipsc2015]Generating Synergy K-D tree

NKOJ 4345 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

数据范围

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


单点查询不是问题,对于多点修改,显然想把它转换为区间修改,这样才能通过打标记保证时间复杂度。

首先容易想到处理“距离不超过l”就用深度就可以搞定了。那么还需要“在子树内”的条件。用DFS序就可以方便地知道一个节点管辖的范围。

因此,如果把每个树上的节点对应为二维空间里的点 (In[x],dep[x]) ,每次修改的就是横坐标在 [In[x],Out[x]] ,纵坐标在 [dep[x],dep[x]+l] 的点,是一个矩形区间。可以用树套树,但是K-D tree更易于实现。

注意修改操作与线段树不同,与平衡树相似:节点本身就代表了一个点。而线段树则是只有底层代表了原来的点。


#include<stdio.h>
#include<algorithm>
#include<cstring>
#define MAXN 100005
#define MAXM 200005
#define ll long long
#define Max(x,y) ((x>y)?(x):(y))
#define Min(x,y) ((x<y)?(x):(y))
using namespace std;

const ll mod=1e9+7;

int N,C,Q,Hash[MAXN];

int D;

struct node{
    int d[2],x[2],y[2],son[2],c,id,lazy;
    bool operator<(const node &x)const{return d[D]<x.d[D];}
}A[MAXN],T[MAXN];

int en[MAXM],nex[MAXM],las[MAXN],Tot;
void Add(int x,int y)
{
    en[++Tot]=y;
    nex[Tot]=las[x];
    las[x]=Tot;
}

int VT,In[MAXN],Out[MAXN],dep[MAXN];
void DFS(int x,int f)
{
    int i,y;

    In[x]=++VT;dep[x]=dep[f]+1;
    for(i=las[x];i;i=nex[i])
    {
        y=en[i];
        if(y!=f)DFS(y,x);
    }
    Out[x]=VT;
}

void Update(int p,int s)
{
    T[p].x[0]=Min(T[p].x[0],T[s].x[0]);
    T[p].x[1]=Max(T[p].x[1],T[s].x[1]);
    T[p].y[0]=Min(T[p].y[0],T[s].y[0]);
    T[p].y[1]=Max(T[p].y[1],T[s].y[1]);
}

void Putdown(int p)
{
    int ls=T[p].son[0],rs=T[p].son[1];
    if(ls)T[ls].c=T[ls].lazy=T[p].lazy;
    if(rs)T[rs].c=T[rs].lazy=T[p].lazy;
    T[p].lazy=0;
}

int tot;
int Build(int l,int r,int d)
{
    D=d;
    int p=++tot,mid=l+r>>1;
    nth_element(A+l,A+mid,A+r+1);
    T[p].d[0]=T[p].x[0]=T[p].x[1]=A[mid].d[0];
    T[p].d[1]=T[p].y[0]=T[p].y[1]=A[mid].d[1];
    T[p].c=1;T[p].lazy=0;
    if(l<mid)T[p].son[0]=Build(l,mid-1,d^1),Update(p,T[p].son[0]);else T[p].son[0]=0;
    if(r>mid)T[p].son[1]=Build(mid+1,r,d^1),Update(p,T[p].son[1]);else T[p].son[1]=0;
    return p;
}

int check(int p,int l,int r,int u,int d)
{
    if(T[p].x[0]>=l&&T[p].x[1]<=r&&T[p].y[0]>=d&&T[p].y[1]<=u)return 2;
    if(T[p].x[0]>r||T[p].x[1]<l||T[p].y[0]>u||T[p].y[1]<d)return 0;
    return 1;
}

void Modify(int p,int l,int r,int u,int d,int k)
{
    if(T[p].lazy)Putdown(p);
    int ck=check(p,l,r,u,d);
    if(ck==2){T[p].lazy=k;T[p].c=k;return;}
    if(ck==0)return;
    if(T[p].d[0]>=l&&T[p].d[0]<=r&&T[p].d[1]<=u&&T[p].d[1]>=d)T[p].c=k;
    if(T[p].son[0])Modify(T[p].son[0],l,r,u,d,k);
    if(T[p].son[1])Modify(T[p].son[1],l,r,u,d,k);
}

int Ans;
void Query(int p,int x,int y,int d)
{
    if(Ans)return;
    if(T[p].lazy)Putdown(p);
    if(T[p].d[0]==x&&T[p].d[1]==y){Ans=T[p].c;return;}
    int ls=T[p].son[0],rs=T[p].son[1];
    if(d)
    {
        if(ls&&T[ls].x[0]<=x&&T[ls].x[1]>=x)Query(ls,x,y,d^1);
        if(rs&&T[rs].x[0]<=x&&T[rs].x[1]>=x)Query(rs,x,y,d^1);
    }
    else
    {
        if(ls&&T[ls].y[0]<=y&&T[ls].y[1]>=y)Query(ls,x,y,d^1);
        if(rs&&T[rs].y[0]<=y&&T[rs].y[1]>=y)Query(rs,x,y,d^1);
    }
}

ll TotAns;
void Clear()
{
    VT=0;
    memset(las,0,sizeof(las));Tot=0;
    tot=0;
    TotAns=0;
}

int main()
{
    int t;

    scanf("%d",&t);
    while(t--)
    {
        Clear();

        int i,x,y,op;

        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++)A[i].d[0]=In[i],A[i].d[1]=dep[i];

        Build(1,N,0);

        for(i=1;i<=Q;i++)
        {
            scanf("%d%d%d",&x,&y,&op);
            if(op==0)
            {
                Ans=0;
                Query(1,In[x],dep[x],0);
                TotAns=(TotAns+1ll*Ans*i)%mod;
            }
            else Modify(1,In[x],Out[x],dep[x]+y,dep[x],op);
        }

        printf("%lld\n",TotAns);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值