洛谷P3242 HNOI2015 接水果

4 篇文章 0 订阅
2 篇文章 0 订阅

也是我做的第一个整体二分题吧。

主要是要分析每一个盘子的贡献。包含path(u, v)这条路径的路径,记为(x, y)。(u比v的dfn小,x比y的小)那么有两种情况。

①u不是v的祖先

此时,x与y势必要分别为u的子树、v的子树(包含u、v)。

②u是v的祖先

这个时候就较为麻烦。首先,一个端点肯定在v的子树中(包含v)。

而另一个端点,较为麻烦。令w为u的儿子,且同时为v的祖先。那么这个端点可以取w这棵子树(包含w)外的任何一点。


考虑清楚贡献,我们就可以离线下来整体二分了。

给所有盘子按权值升序排,然后分治。每次看前一半对每一个水果的贡献(即,包含多少盘子)。就像主席树那样,如果包含的盘子数目≥k,就放入[l, mid]中分治,否则放入[mid + 1, r]。(如果丢进右区间,不要忘记对k操作)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 40010

using namespace std;

inline char gc() {
	static char now[1<<16], *S, *T;
	if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
	return *S++;
}
inline int read() {
	int x = 0, f = 1; char c = gc();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc();}
	while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();}
	return x * f;
}

struct edge {int to, next;}e[N<<1];

struct Plate {int x1, y1, x2, y2, v;}plate[N<<1];
inline bool operator < (const Plate &A, const Plate &B) {return A.v < B.v;}
struct Fruit {int x, y, k, id;}fruit[N], temp1[N], temp2[N];
struct scanLine {int x, y1, y2, v, pos;}line[N<<1];
inline bool operator < (const scanLine &A, const scanLine &B) {return (A.x == B.x)?(A.pos < B.pos):(A.x < B.x);}
int head[N], fa[N][16], dep[N], dfn[N], last[N], ans[N], sum[N], Bit[N];
int n, P, Q, cnt = 1, tim = 0, plt = 0, sz;

inline void modify(int l, int r, int v) {
	for(int i = l; i <= n; i+= i & -i) Bit[i]+= v;
	for(int i = r + 1; i <= n; i+= i & -i) Bit[i]-= v;
}
inline int query(int p) {int ret = 0; for(; p; p-= p & -p) ret+= Bit[p]; return ret;}
inline void ins(int x, int y) {e[++cnt].to = y; e[cnt].next = head[x]; head[x] = cnt;}

void dfs(int x, int f, int d) {
	fa[x][0] = f; dep[x] = d; dfn[x] = ++tim;
	for(int i = head[x]; i; i = e[i].next) if(e[i].to != f) dfs(e[i].to, x, d + 1);
	last[x] = tim;
}
inline void init() {
	for(int j = 1; j < 16; ++j)
		for(int i = 1; i <= n; ++i) fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
inline int jump1(int x, int d) {for(int i = 15; d; --i) if(d >= (1<<i)) d-= (1<<i), x = fa[x][i]; return x;}
inline int getlca(int x, int y) {
	if(dep[x] > dep[y]) swap(x, y);
	for(int i = 15; i >= 0; --i) if(dep[y] - dep[x] >= (1<<i)) y = fa[y][i];
	if(x == y) return x;
	for(int i = 15; i >= 0; --i) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}
void solve(int l, int r, int st, int ed) {
	if(st > ed) return ;
	if(l == r) {for(int i = st; i <= ed; ++i) ans[fruit[i].id] = plate[l].v; return ;}
	int mid = (l + r)>>1; sz = 0;
	for(int i = l; i <= mid; ++i) {
		line[++sz] = (scanLine){plate[i].x1, plate[i].x2, plate[i].y2, 1, 0};
		line[++sz] = (scanLine){plate[i].y1, plate[i].x2, plate[i].y2, -1, n + 1};
	}
	for(int i = st; i <= ed; ++i) line[++sz] = (scanLine){fruit[i].x, fruit[i].y, 0, 0, i};
	sort(line+1, line+sz+1);
	for(int i = 1; i <= sz; ++i) {
		if(st <= line[i].pos && line[i].pos <= ed) sum[line[i].pos] = query(line[i].y1);
		else modify(line[i].y1, line[i].y2, line[i].v);
	}
	int tpa, tpb; tpa = tpb = 0;
	for(int i = st; i <= ed; ++i) {
		if(sum[i] >= fruit[i].k) temp1[++tpa] = fruit[i];
		else temp2[++tpb] = (Fruit){fruit[i].x, fruit[i].y, fruit[i].k - sum[i], fruit[i].id};
	}
	for(int i = st; i <= st + tpa - 1; ++i) fruit[i] = temp1[i - st + 1];
	for(int i = st + tpa; i <= ed; ++i) fruit[i] = temp2[i - st - tpa + 1];
	solve(l, mid, st, st + tpa - 1); solve(mid + 1, r, st + tpa, ed);
}
int main() {
	n = read(); P = read(); Q = read();
	for(int i = 1; i < n; ++i) {int x = read(), y = read(); ins(x, y); ins(y, x);}
	dfs(1, 0, 1); init();
	for(int i = 1; i <= P; ++i) {
		int a = read(), b = read(), c = read();
		if(dfn[a] > dfn[b]) swap(a, b); int w = getlca(a, b);
		if(w != a) plate[++plt] = (Plate){dfn[a], last[a], dfn[b], last[b], c};
		else {
			int u = jump1(b, dep[b] - dep[a] - 1);
			plate[++plt] = (Plate){1, dfn[u] - 1, dfn[b], last[b], c};
			if(last[u] < n) plate[++plt] = (Plate){dfn[b], last[b], last[u] + 1, n, c};
		}
	}
	sort(plate+1, plate+plt+1);
	for(int i = 1; i <= Q; ++i) {
		int u = read(), v = read(), k = read();
		if(dfn[u] > dfn[v]) swap(u, v);
		fruit[i] = (Fruit){dfn[u], dfn[v], k, i};
	}memset(Bit, 0, sizeof(Bit));
	solve(1, plt, 1, Q);
	for(int i = 1; i <= Q; ++i) printf("%d\n", ans[i]);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值