P3241 [HNOI2015]开店【动态点分治】

传送门
求所有值在 l ∼ r l \sim r lr 区间的节点,距离某个点的距离和(有边权,也就是边权不一定为1,范围在1e9以内)

分析

这种要求 N − 1 N-1 N1个点到此某点的信息的问题,通常考虑使用【动态点分治】解决
对于每个作为重心的节点,维护他管辖范围内所有节点的距离,年龄信息,可以存入vector 中,在数组中按照年龄排序(原因是,查询要查找年龄区间的,这样才能二分找到),排序好后,求前缀和。最后统计的时候能够直接通过前缀和,得到区间内所有节点的距离和

同样,需要消除当前节点对父节点的影响,简单容斥
也就是,记录当前所有节点,如果对于父节点的查询来说,造成了多少影响,最后真正统计父节点的时候,将已经计算过的那一部分消去

代码,在luogu上,好像是vector的原因,导致不开O2后面那个点一直TLE,时限6s,差个(0.2s),开了O2,每个点1s左右

代码

//P3241
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 3e5+5;
const ll MOD = 1e9+7;
int N, M, K;

int arr[MAX_N];
struct Node {
	int k;
	int sum;
	Node(){
		
	}
	Node(int _k, int _sum) {
		k = _k;
		sum = _sum;
	}
	
	bool operator < (const Node& B) const {
		return k < B.k;
	}
};

int head[MAX_N];
int tot = 0;
struct Edge {
	int to, nxt;
	int w;
}edge[MAX_N<<1];

void addEdge(int u, int v, int w) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	edge[tot].w = w;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	edge[tot].w = w;
	head[v] = tot++;
}

int dep[MAX_N];
int len[MAX_N];
int top[MAX_N];
int son[MAX_N];
int sz[MAX_N];
int parent[MAX_N];

void dfs1(int u, int from, int k) {
	parent[u] = from;
	len[u] = k;
	sz[u] = 1;
	son[u] = 0;
	dep[u] = dep[from] + 1;
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		dfs1(v, u, k + edge[i].w);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) {
			son[u] = v;
		}
	}
}

void dfs2(int u, int tp) {
	top[u] = tp;
	if (son[u]) dfs2(son[u], tp);
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == son[u] || v == parent[u]) continue;
		dfs2(v, v);
	}
}

int LCA(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) swap(x, y);
		x = parent[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

int dis(int x, int y) {
	int lca = LCA(x, y);
	return len[x] + len[y] - len[lca] * 2;
}


bool vis[MAX_N];
int maxx[MAX_N];
int father[MAX_N];
int dsz[MAX_N];

void dfs(int u, int from, int& rt, int sum) {
	sz[u] = 1;
	maxx[u]  = 0;
	int v;
	
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		dfs(v, u, rt, sum);
		sz[u] += sz[v];
		maxx[u] = max(maxx[u], sz[v]);
	}
	maxx[u] = max(maxx[u], sum - sz[u]);
	if (maxx[u] < maxx[rt]) {
		rt = u;
	}
}

vector<Node>A[MAX_N],B[MAX_N];
void save(int rt, int u, int from, int d) {
	A[rt].push_back(Node(arr[u], d));
	if (father[rt]) B[rt].push_back(Node(arr[u], dis(u, father[rt])));
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		save(rt, v, u, d + edge[i].w);
	}
}

void divide(int u, int sum) {
	vis[u] = 1;
	dsz[u] = sum;
	A[u].reserve(sum+1);
	B[u].reserve(sum+1);
	save(u, u, 0, 0);
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if (vis[v=edge[i].to]) continue;
		int rt = 0;
		int vsz = sz[u] > sz[v] ? sz[v] : sum - sz[u];
		dfs(v, u, rt, vsz);
		father[rt] = u;
		divide(rt, vsz);
	}
}

int calc(vector<Node>&vec, int l, int r, int fix) {
	int st = lower_bound(vec.begin(), vec.end(), Node(l, -1)) - vec.begin() - 1;
	int ed = upper_bound(vec.begin(), vec.end(), Node(r, -1)) - vec.begin() - 1;
	int len = ed - st;
	int res = 0;
	if (ed >= 0 && ed < (int) vec.size()) res += vec[ed].sum;
	if (st >= 0 && st < (int) vec.size()) res -= vec[st].sum;
	return res += len * fix;
}

int count(int x, int l, int r) {
	int res = 0, pre = 0;
	int d;
	for (int i = x; i; pre = i, i = father[i]) {
		res += calc(A[i], l, r, d = dis(x, i));
		if (pre) res -= calc(B[pre], l, r, d);
	}
	return res;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

void solve(){
	init();
	sc("%lld%lld%lld",&N, &M, &K); 
	int u, v, w;
	
	for (int i = 1; i <= N; ++i) {
		sc("%lld", &arr[i]);
	}
	
	for (int i = 1; i < N; ++i) {
		sc("%lld%lld%lld", &u, &v, &w);
		addEdge(u, v, w);
	}
	
	dfs1(1, 0, 0);
	dfs2(1, 1);
	int rt = 0;
	maxx[rt] = 1e9;
	dfs(1, 0, rt, N);
	divide(rt, N);
	
	for (int i = 1; i <= N; ++i) {
		sort(A[i].begin(), A[i].end());
		sort(B[i].begin(), B[i].end());
		for (int j = 1; j < A[i].size(); ++j) {
			A[i][j].sum += A[i][j-1].sum;
		}
		for (int j = 1; j < B[i].size(); ++j) {
			B[i][j].sum += B[i][j-1].sum;
		}
	}
	
	int a = 0, b = 0, pre = 0, l, r;
	for (int i = 1; i <= M; ++i) {
		sc("%lld%lld%lld", &u, &a, &b);
		l = min((a+pre)%K, (b+pre)%K);
		r = max((a+pre)%K, (b+pre)%K);
		pr("%lld\n", pre = count(u, l, r));
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();
	return AC;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值