HYSBZ4154-Generating Synergy

本文介绍了一种使用KD-Tree解决特定类型的树上染色与查询问题的方法。通过将树转换为坐标系中的点集合,利用KD-Tree进行高效的操作更新与颜色查询,适用于节点数量和操作次数较大的场景。

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

[Ipsc2015]Generating Synergy

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 712   Solved: 277
[ Submit][ Status][ Discuss]

Description

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

Input

第一行一个数T,表示数据组数
接下来每组数据的第一行三个数n,c,q表示结点个数,颜色数和操作数
接下来一行n-1个数描述2..n的父节点
接下来q行每行三个数a,l,c
若c为0,表示询问a的颜色
否则将距离a不超过l的a的子节点染成c

Output

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

Sample Input

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

Sample Output

32

HINT



第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




Source


解题思路:kd-tree,可以以每个节点的 DFS 序作为横坐标,深度作为纵坐标,那么就可以把一棵树放在坐标系里了,一次 a 节点,距离为 l 的染色就是纵坐标从 dep[a] 到 dep[a] + l,横坐标从 dfsx[a] 到 dfsx[a] + size[a] - 1 的矩形,dep[a] 表示节点 a 的深度,dfsx[a] 指节点 a 的 DFS 序,size[a] 表示以节点 a 为根的子树大小。更新需要标记下传像线段树一样弄一个标记不断向下移即可


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cctype>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 200003;
const int demension = 2;//二维

struct node
{
	int pos[demension];
	int ma[demension], mi[demension];
	int l, r, val, lazy;
}a[N], x;
int cmpDem;//以第cmpDem维作比较
int root, n, c, q, cnt, u, l, L[N], R[N], deep[N];
int s[N], nt[N], e[N];

void dfs(int k,int dep)
{
	L[k] = cnt++, deep[k] = dep;
	for (int i = s[k]; ~i; i = nt[i]) dfs(e[i], dep + 1);
	R[k] = cnt - 1;
}

bool cmp(const node &a, const node&b)
{
	return a.pos[cmpDem] < b.pos[cmpDem];
}

void Merge(int k)
{
	for (int i = 0; i < demension; i++)
	{
		if (a[k].l)
		{
			a[k].ma[i] = max(a[k].ma[i], a[a[k].l].ma[i]);
			a[k].mi[i] = min(a[k].mi[i], a[a[k].l].mi[i]);
		}
		if (a[k].r)
		{
			a[k].ma[i] = max(a[k].ma[i], a[a[k].r].ma[i]);
			a[k].mi[i] = min(a[k].mi[i], a[a[k].r].mi[i]);
		}
	}
}

int build(int l, int r, int k)
{
	if (l > r) return 0;
	int mid = (l + r) / 2;
	//以第mid个元素为中心排序
	cmpDem = k;
	nth_element(a + l, a + mid, a + r + 1, cmp);
	//左右子树
	a[mid].l = build(l, mid - 1, (k + 1) % demension);
	a[mid].r = build(mid + 1, r, (k + 1) % demension);
	Merge(mid);
	return mid;
}

int check(int k)
{
	if (x.mi[0] <= a[k].mi[0] && a[k].ma[0] <= x.ma[0] && x.mi[1] <= a[k].mi[1] && a[k].ma[1] <= x.ma[1]) return 1;
	return 0;
}

int check1(int k)
{
	if (x.pos[0] <= a[k].ma[0] && a[k].mi[0] <= x.pos[0] && x.pos[1] <= a[k].ma[1] && a[k].mi[1] <= x.pos[1]) return 1;
	return 0;
}

int check2(int k)
{
	int ans = 0;
	for (int i = 0; i < demension; i++)
	{
		int temp = 1;
		if (x.ma[i] < a[k].mi[i] || x.mi[i] > a[k].ma[i]) temp = 0;
		ans += temp;
	}
	if (ans == 2) return 1;
	return 0;
}

int check3(int k)
{
	if (x.mi[0] <= a[k].pos[0] && x.ma[0] >= a[k].pos[0] && x.mi[1] <= a[k].pos[1] && x.ma[1] >= a[k].pos[1]) return 1;
	return 0;
}

void update(int k)
{
	if (check(k)) { a[k].lazy = a[k].val = x.val; return; }
	if (a[k].lazy) {a[a[k].l].lazy = a[a[k].r].lazy = a[k].val = a[k].lazy, a[k].lazy = 0;}
	if (check3(k)) a[k].val = x.val;
	if (a[k].l&&check2(a[k].l)) update(a[k].l);
	if (a[k].r&&check2(a[k].r)) update(a[k].r);
}

int query(int k)
{
	if (a[k].lazy) { a[a[k].l].lazy = a[a[k].r].lazy = a[k].val = a[k].lazy, a[k].lazy = 0; }
	if (x.pos[0] == a[k].pos[0]) return a[k].val;
	if (!a[k].l && !a[k].r) return -1;
	int ans = -1;
	if (a[k].l && check1(a[k].l))
		ans = query(a[k].l);
	if (ans != -1) return ans;
	return query(a[k].r);
}

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d%d", &n, &c, &q);
		cnt = 0;
		memset(s, -1, sizeof s);
		for (int i = 2; i <= n; i++)
		{
			scanf("%d", &u);
			nt[cnt] = s[u], s[u] = cnt, e[cnt++] = i;
		}
		dfs(cnt = 1, 1);
		for (int i = 1; i <= n; i++)
		{
			a[i].pos[0] = L[i], a[i].pos[1] = deep[i], a[i].val = 1, a[i].lazy = 0;
			for (int j = 0; j < 2; j++) a[i].ma[j] = a[i].mi[j] = a[i].pos[j];
		}
		LL ans = 0;
		root = build(1, n, 0);
		for(int i = 1; i <= q; i++)
		{
			scanf("%d%d%d", &u, &l, &c);
			if (!c)
			{
				x.pos[0] = L[u], x.pos[1] = deep[u];
				int temp = query(root);
				ans = (ans + 1LL * temp * i % mod) % mod;
			}
			else
			{
				x.mi[0] = L[u], x.ma[0] = R[u];
				x.mi[1] = deep[u], x.ma[1] = deep[u] + l, x.val = c;
				update(root);
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值