HYSBZ4012-开店

[HNOI2015]开店

Time Limit: 70 Sec   Memory Limit: 512 MB
Submit: 1883   Solved: 811
[ Submit][ Status][ Discuss]

Description

 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到

人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的
想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面
向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n
个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,
其中第 i 个地方的妖怪年龄是 x_i。妖怪都是些比较喜欢安静的家伙,所以它们并
不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一
样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就
比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以
幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即
年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u这个地方离这些妖怪比较
远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多
少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个
称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准
备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

Input

 第一行三个用空格分开的数 n、Q和A,表示树的大小、开店的方案个数和妖

怪的年龄上限。 
第二行n个用空格分开的数 x_1、x_2、…、x_n,x_i 表示第i 个地点妖怪的年
龄,满足0<=x_i<A。(年龄是可以为 0的,例如刚出生的妖怪的年龄为 0。) 
接下来 n-1 行,每行三个用空格分开的数 a、b、c,表示树上的顶点 a 和 b 之
间有一条权为c(1 <= c <= 1000)的边,a和b 是顶点编号。 
接下来Q行,每行三个用空格分开的数 u、 a、 b。对于这 Q行的每一行,用 a、
b、A计算出 L和R,表示询问“在地方 u开店,面向妖怪的年龄区间为[L,R]的方
案的方便值是多少”。对于其中第 1 行,L 和 R 的计算方法为:L=min(a%A,b%A), 
R=max(a%A,b%A)。对于第 2到第 Q行,假设前一行得到的方便值为 ans,那么当
前行的 L 和 R 计算方法为: L=min((a+ans)%A,(b+ans)%A), 
R=max((a+ans)%A,(b+ans)%A)。 

Output

对于每个方案,输出一行表示方便值。 

Sample Input

10 10 10
0 0 7 2 1 4 7 7 7 9
1 2 270
2 3 217
1 4 326
2 5 361
4 6 116
3 7 38
1 8 800
6 9 210
7 10 278
8 9 8
2 8 0
9 3 1
8 0 8
4 2 7
9 7 3
4 7 0
2 2 7
3 2 1
2 3 4

Sample Output

1603
957
7161
9466
3232
5223
1879
1669
1282
0

HINT

 满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9

Source


解题思路:树分治


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#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 int maxn = 3e5 + 10;

int n, q, A, a[maxn];
int s[maxn], nt[maxn], e[maxn], v[maxn], cnt;
int mx[maxn], ct[maxn], vis[maxn];
int pre[maxn], nt1[maxn][3];

struct node
{
	int x, y;
	node(int x = 0, int y = 0) :x(x), y(y) {}
	bool operator < (const node &a)const
	{
		return x < a.x;
	}
};
vector<node> t[maxn][3], dis[maxn];
vector<LL> x[maxn][3];

int dfs(int k, int fa, int sum)
{
	int ans = mx[k] = (ct[k] = 1) - 1;
	for (int i = s[k]; ~i; i = nt[i])
	{
		if (e[i] == fa || vis[e[i]]) continue;
		int y = dfs(e[i], k, sum);
		ct[k] += ct[e[i]];
		mx[k] = max(mx[k], ct[e[i]]);
		ans = mx[ans] < mx[y] ? ans : y;
	}
	mx[k] = max(mx[k], sum - ct[k]);
	return mx[k] < mx[ans] ? k : ans;
}

void Find(int k, int fa, int rt, int id, int len)
{
	dis[rt].push_back(node(k, len));
	t[rt][id].push_back(node(a[k], len));
	for (int i = s[k]; ~i; i = nt[i])
	{
		if (vis[e[i]] || e[i] == fa) continue;
		Find(e[i], k, rt, id, len + v[i]);
	}
}

int build(int k, int sum, int fa)
{
	int y = dfs(k, k, sum);
	pre[y] = fa, vis[y] = 1;
	int id = 0;
	dis[y].push_back(node(y, 0));
	for (int i = s[y]; i != -1; i = nt[i], id++)
	{
		if (vis[e[i]]) continue;
		Find(e[i], y, y, id, v[i]);
		sort(t[y][id].begin(), t[y][id].end());
		for (LL j = 0, k = 0; j < t[y][id].size(); j++)
		{
			k += t[y][id][j].y;
			x[y][id].push_back(k);
		}
		nt1[y][id] = build(e[i], ct[e[i]] > ct[y] ? sum - ct[y] : ct[e[i]], y);
	}
	sort(dis[y].begin(), dis[y].end());
	return y;
}

LL solve(int rt, int k, int l, int r, int from)
{
	if (k == -1) return 0;
	LL ans = 0, sum = 0;
	for (int i = 0; i < 3; i++)
	{
		if (i == from || t[k][i].empty()) continue;
		int L = lower_bound(t[k][i].begin(), t[k][i].end(), node(l, 0)) - t[k][i].begin();
		int R = upper_bound(t[k][i].begin(), t[k][i].end(), node(r, 0)) - t[k][i].begin();
		ans += (R ? x[k][i][R - 1] : 0) - (L ? x[k][i][L - 1] : 0);
		sum += R - L;
	}
	int d = dis[k][lower_bound(dis[k].begin(), dis[k].end(), node(rt, 0)) - dis[k].begin()].y;
	ans = ans + (sum + (a[k] >= l&&a[k] <= r))*d;
	for (int i = 0; i < 3; i++)
		if (pre[k] != -1 && nt1[pre[k]][i] == k) from = i;
	return ans + solve(rt, pre[k], l, r, from);
}

int main()
{
	while (~scanf("%d%d%d", &n, &q, &A))
	{
		mx[cnt = 0] = INF;
		for (int i = 1; i <= n; i++)
		{
			s[i] = -1; vis[i] = 0;
			dis[i].clear();
			for (int j = 0; j < 3; j++)
			{
				t[i][j].clear();
				x[i][j].clear();
				nt1[i][j] = 0;
			}
		}
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
		for (int i = 1; i < n; i++)
		{
			int uu, vv, ww;
			scanf("%d%d%d", &uu, &vv, &ww);
			nt[cnt] = s[uu], s[uu] = cnt, e[cnt] = vv, v[cnt++] = ww;
			nt[cnt] = s[vv], s[vv] = cnt, e[cnt] = uu, v[cnt++] = ww;
		}
		build(1, n, -1);
		LL ans = 0;
		while (q--)
		{
			int u, l, r, ll, rr;
			scanf("%d%d%d", &u, &l, &r);
			ll = min((ans + l) % A, (ans + r) % A);
			rr = max((ans + l) % A, (ans + r) % A);
			ans = solve(u, u, ll, rr, -1);
			printf("%lld\n", ans);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值