Luogu P2056 [ZJOI2007]捉迷藏

入坑动态点分治的题目,感觉还不错被卡常后重构代码

首先静态点分治相信大家肯定都会,就是不断找重心然后暴力计算每棵子树内的贡献。

这题如果只有单次询问,我们很容易想到对于每个分治中心的所以儿子的子树中找两条最长链拼起来。

或者是直接以这个点为端点的一条链的最大值。

如果就这么做复杂度将达到\(O(qn\log n)\),完全无法接受。

我们还是考虑利用一下这个思想,点分治的优化时间的主要方式就是它这棵递归树高度均衡,可以直接在上面完成暴力操作。

注意到这道题没有动态加边,所以其实树的形态是不会变化的,所以我们可以在开始时先跑一边静态的点分治,在上面计算答案的同时把每次的分支中心的树结构建出来。

(在实际题目中由于我们一般只是暴力跳父亲节点,所以可以只记录父节点)

然后这个看似暴力的东西其实就是传说中的点分树

那么动态的问题就比较简单了,我们每次开关一盏灯的时候会影响到点分树上一条链的信息。直接跳即可。

考虑维护答案,这个也很简单,我们开三个大根堆,一个维护子树内最长链,一个维护到父节点的最长链,还有一个维护所有的答案。

统计答案的时候每个子树只用取最大值次大值即可。不过要支持删除,可以考虑再开一个维护删除标记的堆免去手写烦恼。

两点间的距离在DFS的时候记录一个到根节点的路径长然后用LCA差分算一下就好了。

总体复杂度为\(O(n\log^2 n)\),足以通过本题。

先送上最早写的思路比较清晰的CODE,但一直被卡\(90pts\)

#include<cstdio>
#include<cctype>
#include<queue>
#define RI register int
#define Tp template <typename T>
#define add(x,y) e[++cnt]=(edge){y,head[x]},head[x]=cnt
using namespace std;
const int N=100005,INF=1e9;
struct edge
{
int to,nxt;
}e[N<<1]; int head[N],n,m,rt,sonsize,mx[N],cnt,x,y,num; bool is_light[N]; char opt;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
char Fin[S],Fout[S],*A,*B; int pt[15],Ftop;
public:
Tp inline void read(T &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
if (!x) return (void)(pc('0'),pc('\n')); if (x<0) pc('-'),x=-x; RI ptop=0;
while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void get_alpha(char &ch)
{
while (!isalpha(ch=tc()));
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef tc
#undef pc
}F;
struct Heap
{
priority_queue <int> big,del;
inline void push(int x)
{
big.push(x);
}
inline void erase(int x)
{
del.push(x);
}
inline int top(void)
{
while (!del.empty()&&big.top()==del.top())
big.pop(),del.pop(); return big.top();
}
inline int sectop(void)
{
int t=top(); pop(); int ret=top(); push(t); return ret;
}
inline void pop(void)
{
while (!del.empty()&&big.top()==del.top())
big.pop(),del.pop(); big.pop();
}
inline int size(void)
{
return big.size()-del.size();
}
inline bool empty(void)
{
return size()?0:1;
}
}T[N],M[N],A;
class LCA_Solver
{
private:
static const int P=17;
int anc[N][P],dep[N];
inline void swap(int &x,int &y)
{
int t=x; x=y; y=t;
}
inline void reset(int now)
{
for (RI i=0;i<P-1;++i) if (anc[now][i])
anc[now][i+1]=anc[anc[now][i]][i]; else break;
}
inline int query(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
x=anc[x][i],y=anc[y][i]; return anc[x][0];
}
public:
#define to e[i].to
inline void DFS(int now,int fa)
{
dep[now]=dep[fa]+1; reset(now);
for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa) anc[to][0]=now,DFS(to,now);
}
#undef to
inline int dis(int x,int y)
{
return dep[x]+dep[y]-(dep[query(x,y)]<<1);
}
}L;
inline void insert(Heap &s)
{
if (s.size()>1) A.push(s.top()+s.sectop());
}
inline void remove(Heap &s)
{
if (s.size()>1) A.erase(s.top()+s.sectop());
}
class Point_Division_Solver
{
private:
int size[N],tofa[N]; bool vis[N];
inline void maxer(int &x,int y)
{
if (y>x) x=y;
}
#define to e[i].to
inline void travel(int now,int fa,int fart)
{
T[rt].push(L.dis(now,fart)); for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) travel(to,now,fart);
}
public:
inline void getrt(int now,int fa)
{
size[now]=1; mx[now]=0; for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) getrt(to,now),size[now]+=size[to],maxer(mx[now],size[to]);
if (maxer(mx[now],sonsize-size[now]),mx[now]<mx[rt]) rt=now;
}
inline void solve(int now,int fa)
{
tofa[now]=fa; vis[now]=1; M[now].push(0); travel(now,fa,fa);
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa&&!vis[to])
mx[rt=0]=INF,sonsize=size[to],getrt(to,now),to=rt,solve(rt,now),M[now].push(T[to].top()); insert(M[now]);
}
#undef to
inline void Off(int now)
{
remove(M[now]); M[now].push(0); insert(M[now]);
for (RI i=now;i;i=tofa[i])
{
remove(M[tofa[i]]); if (!T[i].empty()) M[tofa[i]].erase(T[i].top());
T[i].push(L.dis(now,tofa[i])); if (!T[i].empty())
M[tofa[i]].push(T[i].top()); insert(M[tofa[i]]);
}
}
inline void On(int now)
{
remove(M[now]); M[now].erase(0); insert(M[now]);
for (RI i=now;i;i=tofa[i])
{
remove(M[tofa[i]]); if (!T[i].empty()) M[tofa[i]].erase(T[i].top());
T[i].erase(L.dis(now,tofa[i])); if (!T[i].empty())
M[tofa[i]].push(T[i].top()); insert(M[tofa[i]]);
}
}
}S;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),num=n,i=1;i<n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
for (sonsize=n,L.DFS(1,0),mx[rt=0]=INF,S.getrt(1,0),S.solve(rt,0),F.read(m),i=1;i<=m;++i)
{
F.get_alpha(opt); if (opt^'C') { F.write(num<=1?num-1:A.top()); continue; }
F.read(x); if (is_light[x]) S.Off(x),++num; else S.On(x),--num; is_light[x]^=1;
}
return F.Fend(),0;
}

后来没办法重构了代码,加了一堆类似剪枝的东西上去终于搞过去了。

CODE

#include<cstdio>
#include<cctype>
#include<queue>
#define RI register int
#define Tp template <typename T>
#define add(x,y) e[++cnt]=(edge){y,head[x]},head[x]=cnt
using namespace std;
const int N=100005,INF=1e9;
struct edge
{
int to,nxt;
}e[N<<1]; int head[N],n,m,rt,sonsize,mx[N],cnt,x,y,num; bool is_light[N]; char opt;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
char Fin[S],Fout[S],*A,*B; int pt[15],Ftop;
public:
Tp inline void read(T &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
if (!x) return (void)(pc('0'),pc('\n')); if (x<0) pc('-'),x=-x; RI ptop=0;
while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void get_alpha(char &ch)
{
while (!isalpha(ch=tc()));
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef tc
#undef pc
}F;
struct Heap
{
priority_queue <int> big,del;
inline void push(int x)
{
big.push(x);
}
inline void erase(int x)
{
del.push(x);
}
inline int top(void)
{
while (!del.empty()&&!(big.top()^del.top()))
big.pop(),del.pop(); return big.empty()?0:big.top();
}
inline int get(void)
{
int s=size(); if (!s) return 0; if (s==1) return top();
int t=top(); pop(); int ret=top()+t; push(t); return ret;
}
inline void pop(void)
{
while (!del.empty()&&!(big.top()^del.top()))
big.pop(),del.pop(); big.pop();
}
inline int size(void)
{
return big.size()-del.size();
}
}T[N],M[N],A;
class LCA_Solver
{
private:
static const int P=17;
int anc[N][P],dep[N];
inline void swap(int &x,int &y)
{
int t=x; x=y; y=t;
}
inline void reset(int now)
{
for (RI i=0;i<P-1;++i) if (anc[now][i])
anc[now][i+1]=anc[anc[now][i]][i]; else break;
}
inline int query(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
x=anc[x][i],y=anc[y][i]; return anc[x][0];
}
public:
#define to e[i].to
inline void DFS(int now,int fa)
{
dep[now]=dep[fa]+1; reset(now);
for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa) anc[to][0]=now,DFS(to,now);
}
#undef to
inline int dis(int x,int y)
{
return dep[x]+dep[y]-(dep[query(x,y)]<<1);
}
}L;
class Point_Division_Solver
{
private:
int size[N],tofa[N]; bool vis[N];
inline void maxer(int &x,int y)
{
if (y>x) x=y;
}
#define to e[i].to
inline void travel(int now,int fa,int fart)
{
T[rt].push(L.dis(now,fart)); for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) travel(to,now,fart);
}
public:
inline void getrt(int now,int fa)
{
size[now]=1; mx[now]=0; for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) getrt(to,now),size[now]+=size[to],maxer(mx[now],size[to]);
if (maxer(mx[now],sonsize-size[now]),mx[now]<mx[rt]) rt=now;
}
inline void solve(int now,int fa)
{
tofa[now]=fa; vis[now]=1; M[now].push(0); travel(now,fa,fa);
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa&&!vis[to])
mx[rt=0]=INF,sonsize=size[to],getrt(to,now),to=rt,solve(rt,now),M[now].push(T[to].top());
A.push(M[now].get());
}
#undef to
inline void Off(int now)
{
M[now].push(0); if (M[now].size()==2) A.push(M[now].top());
for (RI i=now;tofa[i];i=tofa[i])
{
int fa=tofa[i],D=L.dis(fa,now),temp=T[i].top();
T[i].push(D); if (D<=temp) continue;
int mx=M[fa].get(),size=M[fa].size();
if (temp) M[fa].erase(temp); M[fa].push(D);
int Mx=M[fa].get(); if (Mx>mx)
{
if (size>=2) A.erase(mx);
if (M[fa].size()>=2) A.push(Mx);
}
}
}
inline void On(int now)
{
if (M[now].size()==2) A.erase(M[now].top()); M[now].erase(0);
for (RI i=now;tofa[i];i=tofa[i])
{
int fa=tofa[i],D=L.dis(fa,now),temp=T[i].top();
T[i].erase(D); if (D!=temp) continue;
int mx=M[fa].get(),size=M[fa].size();
M[fa].erase(D); if (temp=T[i].top()) M[fa].push(temp);
int Mx=M[fa].get(); if (Mx<mx)
{
if (size>=2) A.erase(mx);
if (M[fa].size()>=2) A.push(Mx);
}
}
}
}S;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),num=n,i=1;i<n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
for (sonsize=n,L.DFS(1,0),mx[rt=0]=INF,S.getrt(1,0),S.solve(rt,0),F.read(m),i=1;i<=m;++i)
{
F.get_alpha(opt); if (opt^'C') { F.write(num<=1?num-1:A.top()); continue; }
F.read(x); if (is_light[x]) S.Off(x),++num; else S.On(x),--num; is_light[x]^=1;
}
return F.Fend(),0;
}
上一篇:20175314薛勐 MyCP(课下作业,必做)


下一篇:从身份证号码中获取性别、出生日期、籍贯,并更新mongodb