主要想法:假设给你足够时间,那么就可以对每个点建议一颗线段树来查询了,但是需要将点全部按照某个特定的序列存进线段树,如代码是以树的深搜顺序作为指定顺序,这样以来我们既可以将数据查询分成诺干个区间进行查询了,减少了大量查询。那么这样你都知道了,其实只需要利用主席树将每个点的情况都存进去主席树里面,因为对于相邻的点来说有着太多的重复情况了,所以我们可以利用这个数据结构来优化时间。
#include<bits/stdc++.h> #define mm ((l + r) >> 1) #define inf 0x3f3f3f3f #define maxn 100070 #define maxm 5000700 using namespace std; vector<int>Gra[maxn]; pair<int, int>a[maxn]; int st[maxn], ed[maxn], dep[maxn], lab[maxn], rt[maxn]; int sum[maxm], mi[maxm], ma[maxm], add[maxm], ch[maxm][2]; int N, Q, ntr, tot, lastans, bb, aa; ///求深度、设置编号顺序 void dfs1(int u, int no, int d){ dep[u] = d; st[u] = ++ntr; lab[ntr] = u; for(int i = Gra[u].size() - 1; i >= 0; i--){ int v = Gra[u][i]; if(v != no) dfs1(v, u, d + 1); } ed[u] = ntr; } ///数据跟新 void push_up(int rt, int l, int r){ sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]] + (r - l + 1) * add[rt]; ma[rt] = max(ma[ch[rt][0]], ma[ch[rt][1]]) + add[rt]; mi[rt] = min(mi[ch[rt][0]], mi[ch[rt][1]]) + add[rt]; } ///建树 int buildTree(int l, int r){ int rt = ++tot; if(l == r){ sum[rt] = ma[rt] = mi[rt] = dep[lab[l]] - 1; ch[rt][0] = ch[rt][1] = add[rt] = 0; return rt; } sum[rt] = ma[rt] = mi[rt] = add[rt] = 0; ch[rt][0] = buildTree(l, mm); ch[rt][1] = buildTree(mm + 1, r); push_up(rt, l, r); return rt; } ///更新区间、 更新值、 实际区间、 旧的数据点 int update(int ll, int rr, int v, int l, int r, int old){ int rt = ++tot; sum[rt] = sum[old]; add[rt] = add[old]; ma[rt] = ma[old]; mi[rt] = mi[old]; ch[rt][0] = ch[old][0]; ch[rt][1] = ch[old][1]; if(ll == l && rr == r){ add[rt] += v; sum[rt] += (rr - ll + 1) * v; ma[rt] += v; mi[rt] += v; return rt; } if(rr <= mm) ch[rt][0] = update(ll, rr, v, l, mm, ch[old][0]); else if(ll > mm) ch[rt][1] = update(ll, rr, v, mm + 1, r, ch[old][1]); else{ ch[rt][0] = update(ll, mm, v, l, mm, ch[old][0]); ch[rt][1] = update(mm + 1, rr, v, mm + 1, r, ch[old][1]); } push_up(rt, l, r); return rt; } ///遍历所有点为每个点在主席树中植入一个版本。关于以该点根的情况。 void dfs2(int u, int no){ for(int i = Gra[u].size() - 1; i >= 0; i --){ int v = Gra[u][i]; if(v == no) continue; rt[v] = update(st[v], ed[v], -1, 1, N, rt[u]); if(st[v] > 1) rt[v] = update(1, st[v] - 1, 1, 1, N, rt[v]); if(ed[v] < N) rt[v] = update(ed[v] + 1, N, 1, 1, N, rt[v]); dfs2(v, u); } } ///相当于查询关于某个点版本的线段树 int query(int ll, int rr, int t, int l, int r, int rt){ if(l == ll && r == rr){ if(t == 1) return sum[rt]; else if(t == 2) return mi[rt]; else return ma[rt]; } if(rr <= mm){ int ret = query(ll, rr, t, l, mm, ch[rt][0]); if(t == 1) return ret + (rr - ll + 1) * add[rt]; else return ret + add[rt]; }else if(ll > mm){ int ret = query(ll, rr, t, mm + 1, r, ch[rt][1]); if(t == 1) return ret + (rr - ll + 1) * add[rt]; else return ret + add[rt]; }else{ int retl = query(ll, mm, t, l, mm, ch[rt][0]); int retr = query(mm + 1, rr, t, mm + 1, r, ch[rt][1]); if(t == 1) return retl + retr + (rr - ll + 1) * add[rt]; if(t == 2) return min(retl, retr) + add[rt]; if(t == 3) return max(retl, retr) + add[rt]; } } int main(){ while(~scanf("%d%d",&N,&Q)){ for(int i = 1; i <= N; i ++) Gra[i].clear(); for(int i = 1; i < N; i ++){ scanf("%d%d",&aa,&bb); Gra[aa].push_back(bb);Gra[bb].push_back(aa); } lastans = tot = ntr = 0; dfs1(1, -1, 1); rt[1] = buildTree(1, N); dfs2(1, -1); int K, T, P, x; while(Q -- ){ scanf("%d%d%d", &K, &P, &T); P = (lastans + P) % N + 1; bool rootsign = false; for(int i = 0; i < K; i ++){ scanf("%d", &x); a[i] = make_pair(st[x], ed[x]); rootsign = x == 1 ? true : rootsign; } if(rootsign){ printf("-1 "); lastans = 0; continue; } sort(a, a + K); a[K ++] = make_pair(N + 1, N + 1); ///排完序之后按照中间空开的区间查询关于某个点的某种数据 if(T == 1) lastans = 0; else if(T == 2) lastans = inf; else lastans = -inf; int pre = 1; for(int i = 0; i < K; i ++){ if(pre < a[i].first){ int tmp = query(pre, a[i].first - 1, T, 1, N, rt[P]); if(T == 1) lastans += tmp; else if(T == 2) lastans = min(lastans, tmp); else lastans = max(lastans, tmp); } pre = max(pre, a[i].second + 1); } printf("%d ",lastans); } } return 0; }