1. 程式人生 > >BZOJ3551: [ONTAK2010]Peaks加強版【Kruskal重構樹】【主席樹】

BZOJ3551: [ONTAK2010]Peaks加強版【Kruskal重構樹】【主席樹】

重要的事情說三遍

不保證圖聯通

不保證圖聯通

不保證圖聯通

那些和我一樣認為重構樹是點數的童鞋是要GG


Description

【題目描述】同3545

Input

第一行三個數N,M,Q。

第二行N個數,第i個數為h_i

接下來M行,每行3個數a b c,表示從a到b有一條困難值為c的雙向路徑。

接下來Q行,每行三個數v x k,表示一組詢問。v=v xor lastans,x=x xor lastans,k=k xor lastans。如果lastans=-1則不變。

Output

同3545

Sample Input

Sample Output

HINT

【資料範圍】同3545


思路

bzoj3545強制線上版本

資料鬼畜

然後大概造資料的人沒考慮這個問題

直接kruskal重構樹跑一下

然後因為kruskal重構樹是二叉堆

所以可以到達的點就變成了一個子樹

然後就直接先倍增再用主席樹可持久化一下dfs序查詢區間第k大就可以了


#include<bits/stdc++.h>

using namespace std;

int read() {
  int res = 0; char c = getchar();
  while (!isdigit(c)) c = getchar();
  while (isdigit(c)) res = res * 10 + c - '0', c = getchar();
  return res;
}

const int N = 2e5 + 10;
const int M = 5e5 + 10;

struct Edge {
  int u, v, w, nxt;
  bool operator < (const Edge &b) const {
    return w < b.w;
  }
} P[M], E[M];

int n, m, q, pre[N], h[N], w[N];
int Fa[N], head[N], tot = 0;

int find(int x) {
  return x == Fa[x] ? x : Fa[x] = find(Fa[x]);
}

void addedge(int u, int v) {
  E[++tot] = (Edge) {u, v, 0, head[u]};
  head[u] = tot;
}

int Kruskal() {
  sort(P + 1, P + m + 1);
  for (int i = 1; i <= n * 2; i++) Fa[i] = i;
  int ind = n;
  for (int i = 1, cnt = 0; i <= m; i++) {
    int fau = find(P[i].u), fav = find(P[i].v);
    if (fau == fav) continue;
    ++ind;
    Fa[fau] = Fa[fav] = ind;
    addedge(ind, fau);
    addedge(ind, fav);
    w[ind] = P[i].w;
    if (++cnt == n - 1) break;
  }
  return ind;
}

int ind = 0, id[N];
int bg[N], ed[N];
int fa[N][20];

void dfs(int u, int father) {
  fa[u][0] = father;
  for (int i = 1; i <= 18; i++)
    fa[u][i] = fa[fa[u][i - 1]][i - 1];
  if (u <= n) {
    id[++ind] = u;
    bg[u] = ind;
  } else {
    bg[u] = ind + 1;
  }
  for (int i = head[u]; i; i = E[i].nxt) {
    int v = E[i].v;
    if (v == father) continue;
    dfs(v, u);
  }
  ed[u] = ind;
}

int rt[N], ls[N * 30], rs[N * 30], siz[N * 30], cnt = 0;

void insert(int &t, int last, int l, int r, int pos) {
  t = ++cnt;
  siz[t] = siz[last] + 1;
  if (l == r) return;
  ls[t] = ls[last];
  rs[t] = rs[last];
  int mid = (l + r) >> 1;
  if (pos <= mid) insert(ls[t], ls[last], l, mid, pos);
  else insert(rs[t], rs[last], mid + 1, r, pos);
}

int query(int rtl, int rtr, int l, int r, int k) {
  if (siz[rtr] - siz[rtl] < k) return -1;
  if (l == r) return pre[l];
  int mid = (l + r) >> 1, sizr = siz[rs[rtr]] - siz[rs[rtl]];
  if (k <= sizr) return query(rs[rtl], rs[rtr], mid + 1, r, k);
  else return query(ls[rtl], ls[rtr], l, mid, k - sizr);
}

int findpos(int u, int vl) {
  for (int k = 18; k >= 0; k--)
    if (fa[u][k] && w[fa[u][k]] <= vl) // **
      u = fa[u][k];
  return u;
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin); 
#endif
  n = read(), m = read(), q = read();
  for (int i = 1; i <= n; i++)
    pre[i] = h[i] = read();
  for (int i = 1; i <= m; i++)
    P[i].u = read(), P[i].v = read(), P[i].w = read();
  int root = Kruskal();
  sort(pre + 1, pre + n + 1);
  int num = unique(pre + 1, pre + n + 1) - pre - 1;
  for (int i = 1; i <= n; i++)
    h[i] = lower_bound(pre + 1, pre + num + 1, h[i]) - pre;
  dfs(root, 0);
  for (int i = 1; i <= n; i++)
    insert(rt[i], rt[i - 1], 1, num, h[id[i]]);
  int lastans = 0;
  while (q--) {
    int v, x, k;
    scanf("%d %d %d", &v, &x, &k);
    v ^= lastans, x ^= lastans, k ^= lastans;
    v = findpos(v, x);
    printf("%d\n", lastans = query(rt[bg[v] - 1], rt[ed[v]], 1, num, k));
    if (lastans < 0) lastans = 0;
  }
  return 0;
}