P3242 HNOI2015接水果
分块求区间第K小,然后用树上莫队(欧拉序分块,特判lca)做(这个做法可能会被卡,不过莫队被卡应该很正常吧?)
令离散化后值域为 (C_{max})
利用分块可以把求第K小的复杂度降到 (O(sqrt{C_{max}})) 也就是 (O(sqrt{P}))
然后就是树上莫队基本操作。
顺便一提,分块求第K小可以去做 这道题
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const ll MAXN = 1e6+10;
ll N, P, Q, head[MAXN], SIZ, c[MAXN], cv[2000], cnt = -1, out[MAXN] ,in[MAXN], block[MAXN], num, fa[22][MAXN], dep[MAXN];
ll seq[MAXN], tot, vis[MAXN], VSIZ, vtot, vblock[MAXN], ans[MAXN], val[MAXN], st[2000], ed[2000], used[MAXN];
vector <ll> V[MAXN], q;
struct edg {ll nt, to;} E[MAXN];
struct ask {
ll l, r, k, id;
friend bool operator < (ask a, ask b) {return block[a.l] == block[b.l] ? block[a.r] < block[b.r] : (block[a.l] < block[b.l]);}
} A[MAXN];
void add(ll, ll);
void calc(ll);
void dfs(ll, ll);
ll gtn(ll);
ll lca(ll, ll);
int main() {
//freopen("P3242.in", "r", stdin);
memset(head, -1, sizeof(head));
scanf("%lld%lld%lld", &N, &P, &Q);
SIZ = (ll)pow(N, 2.0 / 3.0);
VSIZ = (ll)pow(P, 2.0 / 3.0);
for (ll x, y, i = 1; i < N; i++) {
scanf("%lld%lld", &x, &y);
add(x, y);
add(y, x);
}
for (ll x, y, v, i = 1; i <= P; i++) {
scanf("%lld%lld%lld", &x, &y, &v);
V[x].push_back(i);
V[y].push_back(i);
q.push_back(v);
val[i] = v;
}
sort(q.begin(), q.end());
q.erase(unique(q.begin(), q.end()), q.end());
for (ll i = 1; i <= P; i++) val[i] = lower_bound(q.begin(), q.end(), val[i]) - q.begin() + 1;
dfs(1, 0);
for (ll i = 1; i <= num; i++) {
if (i % SIZ == 1) tot++;
block[i] = tot;
}
for (ll i = 1; i <= P; i++) {
if (i % VSIZ == 1) vtot++;
vblock[i] = vtot;
ed[vblock[i]] = i;
}
for (ll i = P; i; i--) st[vblock[i]] = i;
for (ll x, y, i = 1; i <= Q; i++) {
scanf("%lld%lld%lld", &x, &y, &A[i].k);
if (in[x] > in[y]) swap(x, y);
A[i].id = i;
A[i].r = in[y];
if (lca(x, y) == x) A[i].l = in[x];
else A[i].l = out[x];
}
sort(A+1, A+Q+1);
ll l = 1, r = 0;
for (ll i = 1; i <= Q; i++) {
while (l > A[i].l) calc(seq[--l]);
while (r < A[i].r) calc(seq[++r]);
while (l < A[i].l) calc(seq[l++]);
while (r > A[i].r) calc(seq[r--]);
ll x = seq[A[i].l], y = seq[A[i].r]; ll tem = lca(x, y);
ll flag = ((tem == x) | (tem == y));
if (!flag) calc(tem);
ans[A[i].id] = gtn(A[i].k);
if (!flag) calc(tem);
}
for (ll i = 1; i <= Q; i++) printf("%lld
", ans[i]);
return 0;
}
ll gtn(ll kth) {
ll tmp = 0, lv, rv;
for (ll i = 1; i <= vtot; i++) {
tmp += cv[i];
if (tmp >= kth) {tmp -= cv[i], lv = st[i], rv = ed[i]; break;}
}
for (ll i = lv; i <= rv; i++) {
tmp += c[i];
if (tmp >= kth) return q.at(i-1);
}
return -1;
}
void calc(ll pos) {
if (!used[pos]) { // 1
for (unsigned int i = 0; i < V[pos].size(); i++) {
ll tem = V[pos][i]; vis[tem]++;
if (vis[tem] == 2) c[val[tem]]++, cv[vblock[val[tem]]]++;
}
} else { // -1
for (unsigned int i = 0; i < V[pos].size(); i++) {
ll tem = V[pos][i]; vis[tem]--;
if (vis[tem] == 1) c[val[tem]]--, cv[vblock[val[tem]]]--;
}
}
used[pos] ^= 1;
}
void dfs(ll n, ll ff) {
in[n] = ++num, fa[0][n] = ff, seq[num] = n;
for (ll i = 1; i <= 20; i++) fa[i][n] = fa[i-1][fa[i-1][n]];
for (ll i = head[n]; ~i; i = E[i].nt) {
ll v = E[i].to;
if (v == ff) continue;
else {
dep[v] = dep[n] + 1;
dfs(v, n);
}
}
out[n] = ++num, seq[num] = n;
}
ll lca(ll x, ll y) {
if (dep[x] < dep[y]) swap(x, y);
ll dis = dep[x] - dep[y];
for (ll i = 20; ~i; i--) if ((dis >> i) & 1) x = fa[i][x];
if (x == y) return x;
for (ll i = 20; ~i; i--) if (fa[i][x] != fa[i][y]) x = fa[i][x], y = fa[i][y];
return fa[0][x];
}
void add(ll x, ll y) {E[++cnt] = {head[x], y}; head[x] = cnt;}