2022东北四省赛 F. Tree Path 树链剖分 + 线段树
大致题意:
给定三个整数n, k, m分别表示给出n个点,k个路径,和进行m次操作。紧接着读入n-1条边把n个点连成树,在读入k条路径a, b, v,表示从点a到点b有一个权值为v的路径。紧接着读入m个操作,读入op,若op为0则删除k个路径中权值最小的路径,若op为1则在读入一个整数x表示不经过x的路径的其他路径最小值是多少,如没有输出-1。
解题思路:
我们会发现一个路径会对非路径包含的点都可能造成贡献,这个路径的权值一定不会对包含这个路径的点造成贡献,所以考虑树链剖分对除了每个路径所包含的点以外的点都添加一个权值v,但是一个一个加时间复杂度非常高,所以我们考虑用线段树来维护,就不必把每个点递归到叶子结点,所以使用树链剖分加线段树。我们可以对读入的k个路径排个序进行离散化,就可以在线段树上每个结点用一个vector维护。
#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <stack>
#include <queue>
#include <numeric>
#include <cassert>
#include <bitset>
#include <cstdio>
#include <vector>
#include <unordered_set>
#include <cmath>
#include <map>
#include <unordered_map>
#include <set>
#include <deque>
#include <array>
#define all(a) a.begin(), a.end()
#define cnt0(x) __builtin_ctz(x)
#define endl '\n'
#define itn int
#define ll long long
#define ull unsigned long long
#define rep(i, a, b) for(int i = a;i <= b; i ++)
#define per(i, a, b) for(int i = a;i >= b; i --)
#define cntone(x) __builtin_popcount(x)
#define db double
#define AC main(void)
#define lc u << 1
#define rc u << 1 | 1
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
typedef std::pair<int, int > PII;
typedef std::pair<int, std::pair<int, int>> PIII;
typedef std::pair<double, double> PDD;
using ld = double long;
const int MOD = 1e9 + 7;
const double eps = 1e-8;
const int LO = 1 << 20 | 1;
char buffer[LO],*S,*TT;
#define getchar() ((S==TT&&(TT=(S=buffer)+fread(buffer,1,LO,stdin),S==TT))?EOF:*S++)
namespace Fio {
inline std::string sread() {
std::string s = "";
char e = getchar();
while (!isdigit(e) && !isalpha(e) && e != '*') e = getchar();
while (isdigit(e) || isalpha(e) || e == '*') s += e, e = getchar();
return s;
}
inline int read() {
int x = 0, y = 1;
char c = getchar();
while (!isdigit(c)) {
if (c == '-') y = -1;
c = getchar();
}
while (isdigit(c)) {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x *= y;
}
inline ll readll() {
ll x = 0, y = 1;
char c = getchar();
while (!isdigit(c)) {
if (c == '-') y = -1;
c = getchar();
}
while (isdigit(c)) {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x *= y;
}
inline void write(ll x) {
if (x < 0) x = -x, putchar('-');
ll sta[35], top = 0;
do sta[top++] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[--top] + '0');
putchar('\n');
}
inline void write(int x) {
if (x < 0) x = -x, putchar('-');
int sta[35], top = 0;
do sta[top++] = x % 10, x /= 10;
while (x);
while (top) putchar(sta[--top] + '0');
putchar('\n');
}
} using namespace Fio;
inline int max(int a, int b){return a > b ? a : b;}
inline int min(int a, int b){return a > b ? b : a;}
const int N = 2e5 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, m, k, res, _;
int d1[] = {0, 0, 1, -1};
int d2[] = {1, -1, 0, 0};
int w[N], h[N], e[M], ne[M], idx;
int id[N], nw[N], cnt;
int dep[N], sz[N], top[N], fa[N], son[N], ans[N];
struct Node{
int cnt;
}tr[N << 2];
int mn[N], ok, del;
std::vector<int> sum[N << 2];
int pos[N << 1];
inline void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
//统计子树大小 并且 找出重儿子
inline void dfs1(int u, int father, int depth){
dep[u] = depth, fa[u] = father, sz[u] = 1;
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == father) continue;
dfs1(j, u, depth + 1);
sz[u] += sz[j];
if (sz[son[u]] < sz[j]) son[u] = j;
}
}
//dfs序 nw标记dfs为cnt的时候对应的树上点的权值 top标记父亲是谁
inline void dfs2(int u, int t){
id[u] = ++ cnt, nw[cnt] = w[u], top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
void update(int u, int L, int R, int l, int r, int k){
if(L >= l && R <= r){
sum[u].emplace_back(k);
return ;
}
int mid = L + R >> 1;
if(l <= mid) update(u << 1, L, mid, l, r, k);
if(r > mid) update(u << 1 | 1, mid + 1, R, l, r, k);
}
inline int query(int u, int L, int R, int x){
int mx = INF;
while(pos[u] < sum[u].size() && sum[u][pos[u]] <= del) pos[u] ++;
if(L == R){
if(pos[u] == sum[u].size()) return mx;
return sum[u][pos[u]];
}
int mid = L + R >> 1;
if(pos[u] != sum[u].size()) mx = min(mx, sum[u][pos[u]]);
if(x <= mid) mx = min(mx, query(u << 1, L, mid, x));
else mx = min(mx, query(u << 1 | 1, mid + 1, R, x));
return mx;
}
//传进来想要爬的两个点
inline void update_path(int u, int v, int k){
std::vector<PII> g;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
g.push_back({id[top[u]], id[u]});
u = fa[top[u]];
}
if (dep[u] < dep[v]) std::swap(u, v);
g.push_back({id[v], id[u]});
int szz = g.size();
std::sort(g.begin(), g.end());
int r = 1;
for(int i = 0; i < szz; i ++){
if(r < g[i].first) update(1, 1, n, r, g[i].first - 1, k);
r = max(r, g[i].second + 1);
}
if(r <= n) update(1, 1, n, r, n, k);
}
inline void Init(){
dfs1(1, -1, 1);
dfs2(1, 1);
}
void solve(){
memset(h, -1, sizeof h);
n = read(), k = read(), m = read();
for(int i = 1; i < n; i ++){
int a = read(), b = read();
add(a, b);
add(b, a);
}
Init();
std::vector<std::array<int, 3>> q(k);
for(int i = 1; i <= k; i ++){
int a = read(), b = read(), v = read();
q[i - 1] = {v, a, b};
}
std::sort(q.begin(), q.end());
for(auto &[v, a, b]: q){
mn[++ ok] = v;
update_path(a, b, ok);
}
int last = 0;
while(m --){
int op = read();
if(op == 1){
int x = read();
x ^= last;
int ans = query(1, 1, n, id[x]);
if(ans == INF) write(-1), last = -1;
else write(mn[ans]), last = mn[ans];
}else{
del ++;
}
}
}
int AC{
_ = 1;
while(_ --)
solve();
return 0;
}