SDOI2018 原題識別(主席樹)
題目大意
給定個節點的樹,其中包含一條非隨機生成的長度為的鏈,剩下的節點均隨機父節點連邊。每個節點有一個隨機的顏色,維護:
1.給定,求之間不同顏色數。
2.給定,對於所有滿足分別在到根的路徑上的點,求其詢問1的答案之和。
題解
碼量比較大qwq……
我們先從鏈上的情況入手考慮。
鏈的情況
對於第一問,這是經典二維數點題。考慮表示之前第一個和它顏色相同的位置。我們以為座標建點,詢問不同顏色數就相當於詢問座標位於且座標小於的點個數。直接主席樹維護即可。
對於第二問,我們考慮點對答案的貢獻。不妨設,我們分三種情況討論:
1.,此時貢獻應該是。
2.且,此時貢獻應該是。
3.且,此時貢獻應該是。
如果直接把三種答案加起來的話會發現2,3兩種情況中的部分算重了,減1即可。於是我們就需要維護上面的東西(2,3兩個情況其實可以合起來):
第一種是
這個東西可以通過主席樹維護四個值來計算:個數,的和,的和,的和。
我們再來看第二種。
這個東西沒有了對的限制條件,因此直接字首和維護即可。(當然如果你非要主席樹的話我也不能攔著qwq)
到此為止,鏈的情況被我們在的時間內做完了。
推廣到樹
注意到樹除了那條鏈其它都是隨機的,因此每個點到鏈距離的期望是的。每個顏色也是隨機的,因此每個顏色出現次數的期望是的。
也就是說,對於兩個點的LCA,記為,必有一個點到其距離為。不妨就設這個點為,考慮第一問怎麼做。
我們先計算出中不同的顏色數(注意下面的區間都指的是一條鏈),這個可以直接主席樹。接下來做的事就是暴力列舉中的每個顏色,看看它是否在中出現了,直接統計。判斷方法就是暴力列舉所有顏色和它相同的點即可。
因此第一問的複雜度也是的。
考慮第二問,我們可以劃分成如下三個子問題:
1.。這實際上就是鏈的情況,主席樹統計即可。
2.。這其實也是一條鏈,我們可以稍微轉化一下,先求出的答案,然後減去多算的。
多算的東西是,直接字首和就能維護。
3.。這個情況很難算,我們也考慮分開計算貢獻。考慮存在於中的點的貢獻為,主席樹維護即可。
再考慮存在於中點的貢獻,首先它必須是所有與它顏色相同的點中第一個在中出現的,它不能在中包含和它顏色相同的點。不妨令為中第一個和它顏色相同的點(如果不存在則為),那麼其貢獻為。
暴力列舉點是的,找第一次出現時的,因此總複雜度還是的,只是常數比較大。
#include <bits/stdc++.h>
namespace IOStream {
const int MAXR = 1 << 23;
char _READ_[MAXR], _PRINT_[MAXR];
int _READ_POS_, _PRINT_POS_, _READ_LEN_;
inline char readc() {
#ifndef ONLINE_JUDGE
return getchar();
#endif
if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
char c = _READ_[_READ_POS_++];
if (_READ_POS_ == MAXR) _READ_POS_ = 0;
if (_READ_POS_ > _READ_LEN_) return 0;
return c;
}
template<typename T> inline void read(T &x) {
x = 0; register int flag = 1, c;
while (((c = readc()) < '0' || c > '9') && c != '-');
if (c == '-') flag = -1; else x = c - '0';
while ((c = readc()) >= '0' && c <= '9') x = x * 10 + c - '0';
x *= flag;
}
template<typename T1, typename ...T2> inline void read(T1 &a, T2 &...x) {
read(a), read(x...);
}
inline int reads(char *s) {
register int len = 0, c;
while (isspace(c = readc()) || !c);
s[len++] = c;
while (!isspace(c = readc()) && c) s[len++] = c;
s[len] = 0;
return len;
}
inline void ioflush() {
fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0;
fflush(stdout);
}
inline void printc(char c) {
_PRINT_[_PRINT_POS_++] = c;
if (_PRINT_POS_ == MAXR) ioflush();
}
inline void prints(char *s) {
for (int i = 0; s[i]; i++) printc(s[i]);
}
template<typename T> inline void print(T x, char c = '\n') {
if (x < 0) printc('-'), x = -x;
if (x) {
static char sta[20];
register int tp = 0;
for (; x; x /= 10) sta[tp++] = x % 10 + '0';
while (tp > 0) printc(sta[--tp]);
} else printc('0');
printc(c);
}
template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
print(x, ' '), print(y...);
}
}
using namespace IOStream;
using namespace std;
typedef long long ll;
#define cls(a) memset(a, 0, sizeof(a))
const int MAXN = 200005, MAXT = 2000005;
struct Edge { int to, next; } edge[MAXN];
int dfn[MAXN], st[20][MAXN], head[MAXN], lg[MAXN], tot, n, m, K, T;
void addedge(int u, int v) {
edge[++tot] = (Edge) { v, head[u] };
head[u] = tot;
}
int lst[MAXN], col[MAXN], dep[MAXN], rt[MAXN], app[MAXN], ed[MAXN];
struct Value {
ll sum1, sum2, sum3, sum4;
Value() { sum1 = sum2 = sum3 = sum4 = 0; }
Value& operator+=(const Value &v) {
sum1 += v.sum1, sum2 += v.sum2, sum3 += v.sum3, sum4 += v.sum4;
return *this;
}
Value& operator-=(const Value &v) {
sum1 -= v.sum1, sum2 -= v.sum2, sum3 -= v.sum3, sum4 -= v.sum4;
return *this;
}
} nd[MAXT];
//sum1=1,sum2=p[i],sum3=i,sum4=p[i]*i
ll pre2[MAXN], pre3[MAXN]; int ptot;
//pre1=1,pre2=i-p[i],pre3=i(i-p[i])
int ls[MAXT], rs[MAXT], par[MAXN], vis[MAXN];
//presistence segment tree
int update(int p, int x, int y, int l = 0, int r = n) {
int q = ++ptot; nd[q] = nd[p];
++nd[q].sum1, nd[q].sum2 += y, nd[q].sum3 += x, nd[q].sum4 += (ll)x * y;
if (l == r) return q;
int mid = (l + r) >> 1;
if (y <= mid) ls[q] = update(ls[p], x, y, l, mid), rs[q] = rs[p];
else rs[q] = update(rs[p], x, y, mid + 1, r), ls[q] = ls[p];
return q;
}
void query(Value &v, int p, int q, int a, int b, int l = 0, int r = n) {//x in (p,q],y in [a,b]
if (a > r || b < l || p == q) return;
if (a <= l && b >= r) { v += nd[q], v -= nd[p]; return; }
int mid = (l + r) >> 1;
query(v, ls[p], ls[q], a, b, l, mid);
query(v, rs[p], rs[q], a, b, mid + 1, r);
}
vector<int> pla[MAXN];
void dfs(int u, int fa) {
st[0][dfn[u] = ++tot] = u, dep[u] = dep[fa] + 1;
pla[col[u]].push_back(u), lst[u] = app[col[u]];
int t = app[col[u]]; app[col[u]] = u;
pre2[u] = pre2[fa] + dep[u] - dep[lst[u]];
pre3[u] = pre3[fa] + (ll)(dep[u] - dep[lst[u]]) * dep[u];
rt[u] = update(rt[fa], dep[u], dep[lst[u]]);
for (int i = head[u]; i; i = edge[i].next) {
dfs(edge[i].to, u);
st[0][++tot] = u;
}
app[col[u]] = t, ed[u] = tot;
}
int get_min(int x, int y) { return dep[x] < dep[y] ? x : y; }
int get_lca(int x, int y) {
x = dfn[x], y = dfn[y];
if (x > y) swap(x, y);
int l = lg[y - x + 1];
return get_min(st[l][x], st[l][y - (1 << l) + 1]);
}
int on_link(int x, int y, int p) {//x is ancestor of y
return dfn[x] <= dfn[p] && ed[x] >= dfn[p] &&
dfn[p] <= dfn[y] && ed[p] >= dfn[y];
}
int solve1(int x, int y) {
++tot;
int la = get_lca(x, K), lb = get_lca(y, K);
if (la > lb) swap(la, lb), swap(x, y);
int l = get_lca(x, y);
Value v; query(v, rt[par[l]], rt[y], 0, dep[l] - 1);
int res = v.sum1;
for (int i = x; i != l; i = par[i]) if (vis[col[i]] != tot) {
vis[col[i]] = tot;
int flag = 1;
for (int j : pla[col[i]])
if (on_link(l, y, j)) { flag = 0; break; }
res += flag;
}
return res;
}
ll calc_link(int x, int y, const Value &v) {//x is ancestor of y
int a = dep[x], b = dep[y];
ll res = (v.sum1 * a - v.sum2) * (b + 1) - v.sum3 * a + v.sum4;
return res + (a + b + 2) * pre2[x] - 2 * pre3[x] - a;
}
ll solve2(int x, int y) {
++tot;
int la = get_lca(x, K), lb = get_lca(y, K);
if (la > lb) swap(la, lb), swap(x, y);
int l = get_lca(x, y), pl = par[l], dl = dep[l];
Value v1, v2;
query(v1, rt[pl], rt[y], 0, dl - 1);
query(v2, rt[pl], rt[x], 0, dl - 1);
ll res = calc_link(pl, y, v1) + calc_link(pl, x, v2) -
2 * (dl * pre2[pl] - pre3[pl]) + dl - 1;
res += ((dep[y] + 1) * v1.sum1 - v1.sum3) * (dep[x] - dl + 1);
int tp = 0;
for (int i = x; i != l; i = par[i]) app[++tp] = i;
app[++tp] = l;
while (tp > 0) {
int i = app[tp--];
if (vis[col[i]] != tot) {
vis[col[i]] = tot;
int mn = dep[y] + 1;
for (int j : pla[col[i]])
if (on_link(l, y, j) && mn > dep[j]) mn = dep[j];
res += (ll)(mn - dl) * (dep[x] - dep[i] + 1);
}
}
return res;
}
unsigned int SA, SB, SC;
unsigned int rng61(){
SA ^= SA << 16;
SA ^= SA >> 5;
SA ^= SA << 1;
unsigned int t = SA;
SA = SB;
SB = SC;
SC ^= t ^ SA;
return SC;
}
void gen(){
read(n, K, SA, SB, SC);
for(int i = 2; i <= K; i++) addedge(par[i] = i - 1, i);
for(int i = K + 1; i <= n; i++)
addedge(par[i] = rng61() % (i - 1) + 1, i);
for(int i = 1; i <= n; i++) col[i] = rng61() % n + 1;
}
int main() {
for (read(T); T--;) {
tot = 0, cls(head), cls(vis), cls(app);
gen();
for (int i = 1; i <= n; i++) pla[i].clear();
dfs(1, ptot = tot = 0);
for (int i = 2; i <= tot; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 1; i < 20; i++)
for (int j = 1; j + (1 << i) - 1 <= tot; j++)
st[i][j] = get_min(st[i - 1][j], st[i - 1][j + (1 << i >> 1)]);
tot = 0;
for (read(m); m--;) {
int a, b, c; read(a, b, c);
if (a == 1) print(solve1(b, c));
else print(solve2(b, c));
}
}
ioflush();
return 0;
}
相關文章
- SDOI2018:原題識別
- 主席樹
- 主席樹模板
- 靜態主席樹模板
- 動態主席樹模板
- SDOI2018 榮譽稱號(樹形dp)
- HDU2665 Kth number【主席樹】
- HDU4417 Super Mario【主席樹】
- BZOJ4299: Codechef FRBSUM(主席樹)
- D-query SPOJ - DQUERY (主席樹)
- 【資料結構】淺談主席樹資料結構
- 洛谷P4197 Peaks(Kruskal重構樹 主席樹)
- bzoj3524: [Poi2014]Couriers(主席樹)
- HDU-4348 - To the moon (主席樹+區間修改)
- 【主席數】可持續化線段樹
- 演算法隨筆——主席樹(可持久化線段樹)演算法持久化
- 【主席樹】P3919 【模板】可持久化線段樹 1持久化
- bzoj3545: [ONTAK2010]Peaks(主席樹+最小生成樹)
- bzoj4477: [Jsoi2015]字串樹(主席樹+Hash+Lca)JS字串
- bzoj3439: Kpm的MC密碼(主席樹+DFS序+字典樹)密碼
- 迴歸問題知識樹
- 【題解】Solution Set - NOIP2024集訓Day3 權值線段樹、動態開點、主席樹
- bzoj2809: [Apio2012]dispatching(DFS序+主席樹)API
- 求區間不同數的個數【主席樹求解】
- bzoj5178: [Jsoi2011]棒棒糖(主席樹)JS
- bzoj1112: [POI2008]磚塊Klo(主席樹)
- bzoj2588: Spoj 10628. Count on a tree(主席樹+LCA)
- 2024年3月21日 懸繩法 + 珂朵莉樹(ODT) + 主席樹
- 答題卡識別
- 字元識別,口算題識別論文小梗概字元
- 可持久化線段————主席樹(洛谷p3834)持久化
- bzoj3110: [Zjoi2013]K大數查詢(主席樹+樹狀陣列)陣列
- 洛谷 P3919 可持久化線段樹 1 之主席樹模板(初級)持久化
- bzoj1146: [CTSC2008]網路管理Network(dfs序+主席樹+樹狀陣列)陣列
- bzoj1803: Spoj1487 Query on a tree III(DFS序+主席樹)
- bzoj4448: [Scoi2015]情報傳遞(主席樹+Lca)
- bzoj5177: [Jsoi2013]貪心的導遊(主席樹)JS
- bzoj3207: 花神的嘲諷計劃Ⅰ(hash+主席樹)