「WC2020T1」有根樹
「WC2020T1」有根樹
source:LOJ3329
-
記與根節點在同一個聯通塊內的點的個數為 \(cnt\),與根節點不在同一個聯通塊內的點的 \(w\) 的最大值為 \(val\)
-
如何滿足 \(max\{cnt,val\}\) 最小的要求?
-
容易發現,可以通過往與根節點所在的連通塊刪除或增加一個節點,使得\(cnt\)增加或者減少\(1\)
-
所以,\(cnt\) 是可以控制的,而\(val\)是難以實現\(1\)範圍內的變化
-
我們要求 \(cnt\geq val\),找到最小的滿足條件的 \(cnt\),即為答案
-
如何維護動態的增加和刪除節點?
-
對於動態的增加與刪除節點,答案最多隻會變化\(1\)
-
對於當前的樹的每一條葉子節點到根節點的路徑上,存在兩個相鄰節點(可能有一方為空),一個節點和根節點在同一個聯通塊內而另一個不在,維護這兩個邊界節點就可以快速地維護答案的變化
-
如何維護邊界節點?
-
樹剖,維護一條重鏈上的邊界節點(注意判斷重鏈上沒有邊界節點的情況),用樹狀陣列維護每個節點的答案
在刪除節點的時候 \(set\) 等查詢前驅後繼更新
用連結串列或者 \(set\) 維護與根節點不在一個連通塊的點的\(w\)最大值
記錄與根節點在同一個連通塊的節點個數,判斷大小並更新答案
-
線段樹,維護與根節點不在同一個連通塊內的\(w\)的最大值,維護上述過程
「solution x00 」樹剖|樹狀陣列|set (90pnts)
#include<bits/stdc++.h> using namespace std; const int Max_N=5e5,Max_Q=1e6; int n,q,num=0,Num=0,ans=0,A[Max_N+10]={},bln[Max_N+10]={},lin[Max_N+10]={},dfn[Max_N+10]={},size[Max_N+10]={},son[Max_N+10]={},fa[Max_N+10]={},Top[Max_N+10]={},v[Max_N]={},V[Max_N+10]={},Tag[Max_N+10]={}; set<int> a[Max_N+10]; set< pair<int,int> > X,Y; struct kk{ int Id,Next; } e[Max_N*2+10]={}; inline void insert(int x,int y){ e[++Num].Next=lin[x]; lin[x]=Num; e[Num].Id=y; } inline void dfs(int x){ size[x]=1; for(int i=lin[x];i;i=e[i].Next){ if(e[i].Id==fa[x]) continue; fa[e[i].Id]=x; dfs(e[i].Id); size[x]+=size[e[i].Id]; if(size[e[i].Id]>size[son[x]]) son[x]=e[i].Id; } } inline void Dfs(int x,int TOP){ bln[dfn[x]=++num]=x; Top[x]=TOP; if(son[x]) Dfs(son[x],TOP); for(int i=lin[x];i;i=e[i].Next){ if(e[i].Id==fa[x]||e[i].Id==son[x]) continue; Dfs(e[i].Id,e[i].Id); } } inline void Add(int x,int k){ for(;x<=n;x+=x&-x) A[x]+=k; } inline int Ask(int x){ int sum=0; for(;x;x-=x&-x) sum+=A[x]; return sum; } inline void Insert(int x){ int val=Ask(dfn[x]); if(val>ans){ ++ans; Tag[x]=1; if(!v[Top[x]]) X.insert(make_pair(val,v[Top[x]]=dfn[x])); else if(dfn[x]>v[Top[x]]){ X.erase(make_pair(Ask(v[Top[x]]),v[Top[x]])); X.insert(make_pair(val,v[Top[x]]=dfn[x])); } } else { if(!V[Top[x]]) Y.insert(make_pair(val,V[Top[x]]=dfn[x])); else if(dfn[x]<V[Top[x]]){ Y.erase(make_pair(Ask(V[Top[x]]),V[Top[x]])); Y.insert(make_pair(val,V[Top[x]]=dfn[x])); } } } inline void Erase(int x){ int val=Ask(dfn[x]); set<int>::iterator it=a[Top[x]].lower_bound(dfn[x]); ans-=Tag[x]; Tag[x]=0; if(v[Top[x]]==dfn[x]){ int New=*(--it); X.erase(make_pair(val,dfn[x])); if(New==0) v[Top[x]]=0; else v[Top[x]]=New; if(v[Top[x]]) X.insert(make_pair(Ask(v[Top[x]]),v[Top[x]])); } if(V[Top[x]]==dfn[x]){ if(dfn[x]==*(it)) ++it; int New=*(it); Y.erase(make_pair(val,dfn[x])); if(New==n+1) V[Top[x]]=0; else V[Top[x]]=New; if(V[Top[x]]) Y.insert(make_pair(Ask(V[Top[x]]),V[Top[x]])); } } inline void ADD(int x,int k){ if(k==1) a[Top[x]].insert(dfn[x]),Insert(x); else Erase(x),a[Top[x]].erase(dfn[x]); for(;x;x=fa[Top[x]]){ if(v[Top[x]]<=dfn[x]&&v[Top[x]]){ int val=Ask(v[Top[x]]); X.erase(make_pair(val,v[Top[x]])); X.insert(make_pair(val+k,v[Top[x]])); } if(V[Top[x]]<=dfn[x]&&V[Top[x]]){ int val=Ask(V[Top[x]]); Y.erase(make_pair(val,V[Top[x]])); Y.insert(make_pair(val+k,V[Top[x]])); } Add(dfn[Top[x]],k); Add(dfn[x]+1,-k); }//在維護的時候沒有進行答案維護,可能出現了錯誤 if(X.size()&&Y.size()){ set< pair<int,int> >::iterator it=X.begin(),It=Y.end(); --It; pair<int,int> itt=*it,Itt=*It; if(itt.first<Itt.first){ a[Top[bln[itt.second]]].erase(itt.second); a[Top[bln[Itt.second]]].erase(Itt.second); Erase(bln[itt.second]); Erase(bln[Itt.second]); Insert(bln[Itt.second]); Insert(bln[itt.second]); a[Top[bln[itt.second]]].insert(itt.second); a[Top[bln[Itt.second]]].insert(Itt.second); } } if(X.size()){ set< pair<int,int> >::iterator it=X.begin(); pair<int,int> itt=*it; if(ans>itt.first) Erase(bln[itt.second]),Insert(bln[itt.second]); } if(Y.size()){ set< pair<int,int> >::iterator it=Y.end(); --it; pair<int,int> itt=*it; if(ans<itt.first) Erase(bln[itt.second]),Insert(bln[itt.second]); } } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); insert(x,y); insert(y,x); } dfs(1); Dfs(1,1); for(int i=1;i<=n;i++) a[i].insert(0),a[i].insert(n+1); scanf("%d",&q); for(int i=1;i<=q;i++){ int opt,x; scanf("%d%d",&opt,&x); if(opt==1) ADD(x,1); if(opt==2) ADD(x,-1); printf("%d\n",ans); } return 0; }
「solution x10 」樹剖|樹狀陣列|連結串列
-
對於增加/刪除對其他點的影響:
樹剖,每次修改 \([dfn[Top[x]],dfn[x]]\)
更改:樹剖+樹狀陣列 \(O(log_2^2n)\) + 詢問:樹狀陣列 \(O(log_2n)\)
每次只需要標記一下當前增加/刪除的點,用 \(dfs\) 序記錄子樹的區間,區間求和詢問當前點的值
更改:樹狀陣列 \(O(log_2n)\) + 詢問:樹狀陣列 \(O(log_2n)\)
-
對於一條路徑上只會進行最多兩個次更新(一條路徑上只會有兩個邊界點),時間複雜度\(O(log_2n)\)
-
記錄根節點所在的聯通塊的點的個數\(cnt\),與和根節點不在同一個聯通塊的點的權值的最大值\(lim\)
用連結串列\(lup\)記錄與根節點在同一個連通塊內的邊界上的點,\(lim\) 應當比塊內的最大權值小
用連結串列\(ldw\)記錄與根節點不在同一個連通塊的邊界上的點,\(lim\) 應當不小於塊內的最大權值
相對於 \(set\) 查詢,常數較小
-
樹狀陣列維護當前這條重鏈 \(x\) 的前一個標記點和後一個標記點,樹狀陣列上二分查詢
相對於 \(set\) ,常數較小
-
對於增加/刪除需要與當前重鏈的兩個邊界點進行判斷,實現細節較多
-
時間複雜度:\(O(qlog_2n)\)
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> inline void chkmin(T &a, const T &b) { a = a < b ? a : b; }
template<typename T> inline void chkmax(T &a, const T &b) { a = a > b ? a : b; }
namespace IO {
const int MAXR = 10000000;
char rbuf[MAXR], pbuf[MAXR];
int rpos, ppos, rlen;
inline char readc() {
#ifndef ONLINE_JUDGE
return getchar();
#endif
if (!rpos) {
if (feof(stdin)) return -1;
rlen = fread(rbuf, 1, MAXR, stdin);
}
char c = rbuf[rpos++];
if (rpos == rlen) rpos = 0;
return c;
}
template<typename T> inline int read(T &x) {
x = 0; register int flag = 1, c;
while (((c = readc()) < '0' || c > '9') && c != '-')
if (c < 0) return -1;
if (c == '-') flag = -1; else x = c - '0';
while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
x *= flag; return 0;
}
template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
return read(a) | read(x...);
}
inline void ioflush() { fwrite(pbuf, 1, ppos, stdout), ppos = 0; fflush(stdout); }
inline void printc(char c) {
if (!c) return;
pbuf[ppos++] = c;
if (ppos == MAXR) ioflush();
}
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');
if (c) printc(c);
}
}
const int MAXN = 500005;
struct Edge { int to, next; } edge[MAXN << 1];
int par[MAXN], up[MAXN], vup[MAXN], dw[MAXN], vdw[MAXN];
int dfn[MAXN], top[MAXN], lg[MAXN], head[MAXN], sz[MAXN];
int wson[MAXN], mem[MAXN], num[MAXN], ed[MAXN], bit[MAXN];
int pla[MAXN], n, tot, lim, cnt, Q;
inline void add_edge(int u, int v) {
edge[++tot] = Edge { v, head[u] };
head[u] = tot;
}
void dfs1(int u, int fa) {
sz[u] = 1, par[u] = fa;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == fa) continue;
dfs1(v, u);
if (sz[v] > sz[wson[u]]) wson[u] = v;
sz[u] += sz[v];
}
}
void dfs2(int u, int fa, int t) {
++num[t], pla[dfn[u] = ++tot] = u, top[u] = t;
if (wson[u]) dfs2(wson[u], u, t);
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v != fa && v != wson[u]) dfs2(v, u, v);
}
ed[u] = tot;
}
struct Link {
int pre[MAXN << 1], nxt[MAXN << 1];
inline void del(int x) {
pre[nxt[x]] = pre[x];
nxt[pre[x]] = nxt[x];
nxt[x] = pre[x] = x;
}
inline void ins(int h, int x) {
h += n + 1;//h代表x節點的w,對於每一種價值維護一個連結串列,記錄所有的價值為h的點的編號
nxt[x] = nxt[h];
pre[nxt[h]] = x;
nxt[h] = x;
pre[x] = h;
}
inline int get(int h) {
return nxt[h + n + 1];
}
} lup, ldw;
inline void update(int x) {
lup.del(x);
if (up[x]) lup.ins(vup[x], x);
ldw.del(x);
if (dw[x] <= num[x]) ldw.ins(vdw[x], x);
}//更新
inline void add(int *bit, int n, int x, int w) {
for (; x <= n; x += x & -x) bit[x] += w;
}
inline int pred(const int *bit, int n, int x) {
for (--x; x && !bit[x]; x -= x & -x);
if (!x) return 0;
int y = x - (x & -x), s = 0;
for (int i = (x & -x) >> 1; i; i >>= 1)
if (s + bit[y | i] < bit[x]) s += bit[y |= i];
return y + 1;
}//查詢前驅
inline int succ(const int *bit, int n, int x) {
int s = 0;
for (; x; x -= x & -x) s += bit[x];
for (int i = lg[n]; i >= 0; i--)
if ((x | 1 << i) <= n && bit[x | 1 << i] <= s) s -= bit[x |= 1 << i];
return x + 1;
}//查詢後繼
inline int ask(const int *bit, int n, int x) {
int res = 0;
for (; x; x -= x & -x) res += bit[x];
return res;
}
inline int get_w(int x) {
return ask(bit, n, ed[x]) - ask(bit, n, dfn[x] - 1);
}
inline void erase_up(int x, bool f = true) {
if (f) dw[x] = up[x], vdw[x] = vup[x];
up[x] = pred(mem + dfn[x] - 1, num[x], up[x]);
vup[x] = !up[x] ? 0 : get_w(pla[dfn[x] + up[x] - 1]);
}
inline void erase_dw(int x, bool f = true) {
if (f) up[x] = dw[x], vup[x] = vdw[x];
dw[x] = succ(mem + dfn[x] - 1, num[x], dw[x]);
vdw[x] = dw[x] > num[x] ? 0 : get_w(pla[dfn[x] + dw[x] - 1]);
}
inline void update_val(int x, int d, int w) {
if (up[x] && d >= up[x]) vup[x] += w;
if (d >= dw[x]) vdw[x] += w;
}//更新關鍵節點
void insert(int x) {
add(bit, n, dfn[x], 1);
int t = top[x], d = dfn[x] - dfn[t] + 1;
add(mem + dfn[t] - 1, num[t], d, 1);
update_val(t, d, 1);
if (vdw[t] > lim) {
++cnt;
erase_dw(t);
} else if (d < dw[t]) {
int s = get_w(x);
if (s <= lim) {
dw[t] = d;
vdw[t] = s;
} else {
++cnt;
if (d > up[t]) {
up[t] = d;
vup[t] = s;
}
}
}
update(t);
while ((x = par[t]) && (t = top[x])) {
update_val(t, d = dfn[x] - dfn[t] + 1, 1);
if (vdw[t] > lim) {
++cnt;
erase_dw(t);
}
update(t);
}
while (cnt > lim) {
int g = lup.get(lim);
if (g > n) { ++lim; continue; }
--cnt;
erase_up(g);
update(g);
}
}
void remove(int x) {
add(bit, n, dfn[x], -1);
int t = top[x], d = dfn[x] - dfn[t] + 1;
add(mem + dfn[t] - 1, num[t], d, -1);
update_val(t, d, -1);
if (d <= up[t]) --cnt;
if (d == up[t]) erase_up(t, false);
else if (d == dw[t]) erase_dw(t, false);
if (up[t] && vup[t] <= lim) {
erase_up(t);
--cnt;
}
update(t);
while ((x = par[t]) && (t = top[x])) {
update_val(t, d = dfn[x] - dfn[t] + 1, -1);
if (up[t] && vup[t] <= lim) {
erase_up(t);
--cnt;
}
update(t);
}
while (cnt < lim) {
int g = ldw.get(lim);
if (g > n) { --lim; continue; }
++cnt;
erase_dw(g);
update(g);
}
}
int main() {
IO::read(n);
for (int i = 1; i < n; i++) {
int u, v; IO::read(u, v);
add_edge(u, v), add_edge(v, u);
}
dfs1(1, 0);
dfs2(1, tot = 0, 1);
for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= n * 2; i++) {
lup.pre[i] = lup.nxt[i] = i;
ldw.pre[i] = ldw.nxt[i] = i;
}
for (int i = 1; i <= n; i++)
if (top[i] == i) dw[i] = num[i] + 1;
IO::read(Q);
while (Q--) {
int t, p; IO::read(t, p);
if (t == 1) insert(p);
else remove(p);
IO::print(cnt);
}
IO::ioflush();
return 0;
}
「solution x20 」樹剖|線段樹(丁曉漫)
- 實現更為簡單
- 時間複雜度:\(O(nlog^2_2n)\)
#include<bits/stdc++.h>
using namespace std;
namespace io{
const int size=1<<22|1;
char iBuf[size],*iS,*iT,c;
char oBuf[size],*oS=oBuf,*oT=oBuf+size;
char getc(){
if(iS==iT){
iS=iBuf;
iT=iBuf+fread(iBuf,1,size,stdin);
}
if(iS==iT)return EOF;
return *iS++;
}
template<class T>void qread(T &x){
int f=1;
for(c=getc();c<'0'||c>'9';c=getc())
if(c=='-')f=-1;
for(x=0;c>='0'&&c<='9';c=getc())
x=(x<<3)+(x<<1)+(c&15);
x*=f;
}
void flush(){
fwrite(oBuf,1,oS-oBuf,stdout);
oS=oBuf;
}
void putc(char x){
*oS++=x;
if(oS==oT)flush();
}
template<class T>void qwrite(T x){
if(x<0)putc('-'),x=-x;
static char qu[55];
char *tmp=qu;
do *tmp++=(x%10)^'0';while(x/=10);
while(tmp--!=qu)putc(*tmp);
}
struct flusher{
~flusher(){flush();}
}_;
}
const int maxn=500005;
const int INF=0x3f3f3f3f;
int n,k,q,ans;
vector<int>edge[maxn];
int sz[maxn],son[maxn],par[maxn],top[maxn],dfn[maxn],rev[maxn];
bool in[maxn];
namespace Segtree{
pair<int,int>mn[maxn*4],mx[maxn*4];
int tag[maxn*4];
void pushup(int cur){
mn[cur]=min(mn[cur<<1],mn[cur<<1|1]);
mx[cur]=max(mx[cur<<1],mx[cur<<1|1]);
mn[cur].first+=tag[cur];
mx[cur].first+=tag[cur];
}
void build(int cur,int l,int r){
if(l==r){
mn[cur]=make_pair(INF,rev[l]);
mx[cur]=make_pair(-INF,rev[l]);
return;
}
int mid=l+r>>1;
build(cur<<1,l,mid);
build(cur<<1|1,mid+1,r);
pushup(cur);
}
void set(int cur,int l,int r,int k,int opmn,int opmx){
if(l==r){
mn[cur]=make_pair((opmn==0?INF:0)+tag[cur],rev[l]);
mx[cur]=make_pair((opmx==0?-INF:0)+tag[cur],rev[l]);
return;
}
int mid=l+r>>1;
if(k<=mid)set(cur<<1,l,mid,k,opmn,opmx);
else set(cur<<1|1,mid+1,r,k,opmn,opmx);
pushup(cur);
}//更改節點的所屬連通塊
void update(int cur,int l,int r,int vl,int vr,int val){
if(l>=vl&&r<=vr){
mn[cur].first+=val;
mx[cur].first+=val;
tag[cur]+=val;
return;
}
int mid=l+r>>1;
if(mid>=vl)update(cur<<1,l,mid,vl,vr,val);
if(mid<vr)update(cur<<1|1,mid+1,r,vl,vr,val);
pushup(cur);
}//更新價值
}
void dfs1(int x,int p){
par[x]=p;
sz[x]=1;
for(int i=0;i<int(edge[x].size());i++){
int y=edge[x][i];
if(y==p)continue;
dfs1(y,x);
sz[x]+=sz[y];
if(!son[x]||sz[y]>sz[son[x]])son[x]=y;
}
}
void dfs2(int x,int p,int t){
top[x]=t;
dfn[x]=++k;
rev[k]=x;
if(son[x])dfs2(son[x],x,t);
for(int i=0;i<int(edge[x].size());i++){
int y=edge[x][i];
if(y==p||y==son[x])continue;
dfs2(y,x,y);
}
}
void work(int x,int f){
while(x){
Segtree::update(1,1,n,dfn[top[x]],dfn[x],f);
x=par[top[x]];
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
io::qread(n);
for(int i=1;i<=n-1;i++){
int u,v;
io::qread(u);io::qread(v);
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0,1);
Segtree::build(1,1,n);
io::qread(q);
while(q--){
int f,x;
io::qread(f);io::qread(x);
int nans=ans;
if(f==1){
work(x,1);
Segtree::set(1,1,n,dfn[x],0,1);
if(Segtree::mx[1].first>=ans+1){
int x=Segtree::mx[1].second;
in[x]=true;
nans++;
Segtree::set(1,1,n,dfn[x],1,0);
}
if(nans>ans&&Segtree::mn[1].first<=ans){
int x=Segtree::mn[1].second;
in[x]=false;
nans--;
Segtree::set(1,1,n,dfn[x],0,1);
} //尋找合法的答案
}
else{
work(x,-1);
nans-=in[x];
in[x]=false;
Segtree::set(1,1,n,dfn[x],0,0);
if(Segtree::mn[1].first<=ans-1){
int x=Segtree::mn[1].second;
in[x]=false;
nans--;
Segtree::set(1,1,n,dfn[x],0,1);
}
if(nans<ans&&Segtree::mx[1].first>=ans){
int x=Segtree::mx[1].second;
in[x]=true;
nans++;
Segtree::set(1,1,n,dfn[x],1,0);
}
}
ans=nans;
io::qwrite(ans);io::putc('\n');
}
return 0;
}