題目鏈結
考慮列舉每個點的答案,最後除以 2 即可。
可以與 \(u\) 構成合法點對的點集為所有經過了 \(u\) 的鏈的並。因為這些鏈兩兩有交,所以它們的並集構成了一棵樹。
考慮維護經過每個點的鏈並集的大小。一條鏈是否出現可以樹上差分,並集的具體大小就以 \(dfs\) 序為下標建線段樹,然後線段樹合併即可。
複雜度 \(o(n\log n)\) 。
#includeusing namespace std;
typedef long long ll;
#define go(u) for(int i = head[u], v = e[i].to; i; i = e[i].lst, v = e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
inline int gi()
while(isdigit(ch))
return x * f;
}template inline bool max(t &a, t b)
template inline bool min(t &a, t b)
const int n = 1e5 + 7;
ll ans;
int n, m, edc, elc, ndc, tim;
int head[n], fa[n], in[n], out[n], fie[n], log[n << 1], dep[n];
typedef pairpii;
pii mi[n << 1][20];
struct edge edge(int lst, int to):lst(lst), to(to){}
}e[n << 1];
void add(int a, int b)
void dfs1(int u)
out[u] = tim;
}int lca(int l, int r)
int rt[n];
vectorg[n];
struct node t[n * 150];
#define ls t[o].l
#define rs t[o].r
void pushup(int o)
void modify(int p, int l, int r, int &o, int v) int mid = l + r >> 1;
if(in[p] <= mid) modify(p, l, mid, ls, v);
else modify(p, mid + 1, r, rs, v);
pushup(o);
}int merge(int l, int r, int x, int y) int mid = l + r >> 1;
t[x].l = merge(l, mid, t[x].l, t[y].l);
t[x].r = merge(mid + 1, r, t[x].r, t[y].r);
return pushup(x), x;
}void dfs2(int u)
for(auto v:g[u]) modify(v, 1, n, rt[u], -2);
if(t[rt[u]].a && t[rt[u]].b)
ans += t[rt[u]].s - dep[fa[lca(t[rt[u]].a, t[rt[u]].b)]] - 1;
}int main()
dfs2(1);
printf("%d\n", ndc);
printf("%lld\n", ans / 2);
return 0;
}
ZJOI2019 語言 解題報告
3個 log 做法比較簡單,但是寫起來還是有點麻煩的。大概就是樹剖把鏈劃分為 log 段,然後任意兩段可以組成乙個矩形,就是個矩形面積並,聽說卡卡就過去了。好像這個可以被優化到兩個 log 算了,估計挺麻煩的。乙個 log 的做法看起來還挺厲害的。考慮欽定某個點算它的貢獻,於是我們要算的是所有經過它...
ZJOI2019 線段樹 解題報告
聽說有人噴這個題簡單,然後我就跑去做,然後自閉感 rp 霧 理性分析一波,可以發現最後形成的 2 k 個線段樹,對應的操作的乙個子集,按時間順序作用到這顆線段樹上。首先考慮研究一下tag的性質,比如兩個操作時間先後是否沒有影響,操作是否可以以某種形式進行合併,然後啥也沒發現。然後考慮一下一顆樹是否可...
ZJOI2019 語言 樹上差分 線段樹合併
loj 3046 題意還是很好懂的,問題也很容易轉化為求每個點能到的點的個數之和,最後除以 2 即可 考慮任意一點i能到的點的個數。這些點所組成的點集等於所有包含節點 i 的鏈的點集的並集。需要哪些資訊才能維護出這個點集?由於每條鏈都包含了節點 i 因此這個點集會組成乙個連通塊 暫且這麼叫吧 這個連...