ybtAu「树上问题」第2章 点分治
A. 【例题1】树上问题
板子,树状数组统计即可。
#include <iostream>
#define N 80005
#define int long long
int n,K,hed[N],tal[N],wt[N],nxt[N],cnte,ans;
void de(int u,int v,int w) {tal[++cnte]=v,wt[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
namespace BIT
{
int f[N],r;
void c(int x,int t) {for(;x<=K;x+=x&-x) f[x]+=t;}
int q(int x) {for(r=0;x>0;x-=x&-x) r+=f[x];return r;}
};
namespace DT
{
int mx,trt,siz[N],son[N],vis[N],st[N],tp;
void grt(int x,int fa,int tot)
{
siz[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]])
grt(tal[i],x,tot),siz[x]+=siz[tal[i]],son[x]=std::max(son[x],siz[tal[i]]);
son[x]=std::max(son[x],tot-siz[x]);
if(son[x]<mx) mx=son[x],trt=x;
}
void gdis(int x,int fa,int dis)
{
st[++tp]=dis;
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]]) gdis(tal[i],x,dis+wt[i]);
}
void solve(int x,int tot)
{
mx=1e9,grt(x,x,tot),x=trt;
vis[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(!vis[tal[i]])
{
int lt=tp;
gdis(tal[i],x,wt[i]);
for(int p=tp;p>lt;p--) ans+=BIT::q(K-st[p]);
for(int p=tp;p>lt;p--) if(st[p]<=K) BIT::c(st[p],1),ans++;
}
for(;tp;tp--) BIT::c(st[tp],-1);
for(int i=hed[x];i;i=nxt[i]) if(!vis[tal[i]]) solve(tal[i],tot-son[tal[i]]);
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n;
for(int i=1,u,v,w;i<n;i++) std::cin>>u>>v>>w,de(u,v,w),de(v,u,w);
std::cin>>K;
DT::solve(1,n);
std::cout<<ans;
}
B. 【例题2】聪聪可可
板子,开两个大小为 \(3\) 的桶即可。注意要加上到分治中心的链和长度为 \(0\) 的链。
#include <iostream>
#define N 80005
#define int long long
int n,hed[N],tal[N],wt[N],nxt[N],cnte,ans;
void de(int u,int v,int w) {tal[++cnte]=v,wt[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
int buc[3];
namespace DT
{
int mx,trt,siz[N],son[N],vis[N],st[3],tp;
void grt(int x,int fa,int tot)
{
siz[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]])
grt(tal[i],x,tot),siz[x]+=siz[tal[i]],son[x]=std::max(son[x],siz[tal[i]]);
son[x]=std::max(son[x],tot-siz[x]);
if(son[x]<mx) mx=son[x],trt=x;
}
void gdis(int x,int fa,int dis)
{
st[dis%3]++;
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]]) gdis(tal[i],x,dis+wt[i]);
}
void solve(int x,int tot)
{
mx=1e9,grt(x,x,tot),x=trt;
vis[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(!vis[tal[i]])
{
gdis(tal[i],x,wt[i]);
ans+=2*st[0];
for(int j=0;j<3;j++) ans+=2*buc[(3-j)%3]*st[j];
for(int j=0;j<3;j++) buc[j]+=st[j],st[j]=0;
}
ans++;
buc[0]=buc[1]=buc[2]=0;
for(int i=hed[x];i;i=nxt[i]) if(!vis[tal[i]]) solve(tal[i],tot-son[tal[i]]);
}
};
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n;
for(int i=1,u,v,w;i<n;i++) std::cin>>u>>v>>w,de(u,v,w),de(v,u,w);
DT::solve(1,n);
int sum=n*n;
int tgcd=gcd(ans,sum);
std::cout<<ans/tgcd<<'/'<<sum/tgcd;
}
C. 重建计划
01 分数规划。
二分答案,把每条边的边权减去 \(mid\),转化为判断是否存在长度 \(\in[L,U]\) 且权值和为正的路径。
点分治,用桶 \(buc_i\) 记录已经处理的子树中,从分治中心出发,长度为 \(i\) 的路径的最大权值;处理一棵子树时,用桶 \(cur_i\) 记录子树内的权值。
令路径的一个端点在已经处理的子树中,另一个端点在当前子树内,那么对于子树内深度为 \(i\) 的节点,可以用 \(cur_i+buc_j(j\in[L-i,R-i])\) 来更新答案。
如果从小到大枚举 \(i\) 的话,发现区间左右端点是递减的,可以使用单调队列求解。
注意实现细节,数组要清干净。
#include <iostream>
#include <vector>
#define N 200005
int n,L,U,hed[N],deh[N],tal[N<<1],wt[N],nxt[N<<1],cnte,rt;
void de(int u,int v,int w) {tal[++cnte]=v,wt[cnte]=w,nxt[cnte]=hed[u],hed[u]=cnte;}
void ed(int u,int v) {tal[++cnte]=v,nxt[cnte]=deh[u],deh[u]=cnte;}
namespace DT
{
int mn,trt,siz[N],son[N],vis[N],st[N],tp,mdep,q[N],tot;
double mid,buc[N],cur[N];
void gtot(int x,int fa)
{
tot++;
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]]) gtot(tal[i],x);
}
void grt(int x,int fa)
{
siz[x]=son[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]])
grt(tal[i],x),siz[x]+=siz[tal[i]],son[x]=std::max(son[x],siz[tal[i]]);
son[x]=std::max(son[x],tot-siz[x]);
if(son[x]<mn) mn=son[x],trt=x;
}
int build(int x)
{
tot=0,gtot(x,x),mn=1e9,grt(x,x),x=trt;
vis[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(!vis[tal[i]])
ed(x,build(tal[i]));
vis[x]=0;
return x;
}
void gdis(int x,int fa,int dep,double dis)
{
mdep=std::max(dep,mdep);
cur[dep]=std::max(cur[dep],dis);
for(int i=hed[x];i;i=nxt[i]) if(tal[i]!=fa&&!vis[tal[i]])
gdis(tal[i],x,dep+1,dis+wt[i]-mid);
}
bool check(int x)
{
vis[x]=1;
int mt=0,fg=0;
for(int i=hed[x];i;i=nxt[i]) if(!vis[tal[i]])
{
mdep=0,gdis(tal[i],x,1,wt[i]-mid);
int hd=1,tl=0,p=mt;
for(int j=1;j<=mdep;j++)
{
while(hd<=tl&&q[hd]+j>U) hd++;
while(p>=L-j&&p>=0)
{
while(hd<=tl&&buc[q[tl]]<=buc[p]) tl--;
q[++tl]=p,p--;
}
if(hd<=tl&&buc[q[hd]]+cur[j]>0) {fg=1;break;}
}
for(int j=1;j<=mdep;j++) buc[j]=std::max(buc[j],cur[j]),cur[j]=-1e9;
mt=std::max(mt,mdep);
if(fg) break;
}
for(int i=1;i<=mt;i++) buc[i]=-1e9;
if(fg) return vis[x]=0,1;
for(int i=deh[x];i;i=nxt[i]) if(check(tal[i])) return vis[x]=0,1;
vis[x]=0;
return fg;
}
};
bool check(double mid)
{
//printf("check %.2lf\n",mid);
DT::mid=mid;
return DT::check(rt);
}
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>L>>U;
double l=1e9,r=0;
for(int i=1;i<=n;i++) DT::buc[i]=DT::cur[i]=-1e9;
for(int i=1,u,v,w;i<n;i++) std::cin>>u>>v>>w,de(u,v,w),de(v,u,w),l=std::min(l,1.0*w),r=std::max(r,1.0*w);
rt=DT::build(1);
while(r-l>1e-4)
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.3lf",l);
}
D. 树上GCD
E. 幸运数字
树剖 + 线段树维护区间线性基。
#include <iostream>
#include <cstring>
#include <cassert>
#define int long long
#define N 40005
int n,q,hed[N],tal[N],nxt[N],cnte,a[N];
void de(int u,int v) {tal[++cnte]=v,nxt[cnte]=hed[u],hed[u]=cnte;}
struct Base
{
int d[61];
Base() {memset(d,0,sizeof d);}
void ins(int x)
{
for(int i=60;i>=0;i--) if(x>>i&1)
{
if(d[i]) x^=d[i];
else return d[i]=x,void();
}
}
int qr()
{
int ret=0;
for(int i=60;i>=0;i--) if(d[i]&&!(ret>>i&1)) ret^=d[i];
return ret;
}
};
Base mg(Base x,Base y)
{
for(int i=60;i>=0;i--) if(y.d[i]) x.ins(y.d[i]);
return x;
}
namespace SGT
{
Base d[N<<2];
#define mid (lb+rb>>1)
void md(int x,int t,int k,int lb,int rb)
{
d[x].ins(k);
if(lb<rb) (t<=mid)?md(x<<1,t,k,lb,mid):md(x<<1|1,t,k,mid+1,rb);
}
Base qr(int x,int l,int r,int lb,int rb)
{
if(l<=lb&&rb<=r) return d[x];
if(r<=mid) return qr(x<<1,l,r,lb,mid);
if(l>mid) return qr(x<<1|1,l,r,mid+1,rb);
return mg(qr(x<<1,l,r,lb,mid),qr(x<<1|1,l,r,mid+1,rb));
}
#undef mid
};
namespace HLD
{
int dfn[N],dep[N],fa[N],son[N],siz[N],top[N],idx;
void d1(int x)
{
siz[x]=1;
for(int i=hed[x];i;i=nxt[i]) if(!siz[tal[i]])
{
fa[tal[i]]=x,dep[tal[i]]=dep[x]+1,d1(tal[i]),siz[x]+=siz[tal[i]];
if(siz[tal[i]]>siz[son[x]]) son[x]=tal[i];
}
}
void d2(int x,int tp)
{
if(!x) return;
dfn[x]=++idx,d2(son[x],top[x]=tp);
for(int i=hed[x];i;i=nxt[i]) if(!top[tal[i]]) d2(tal[i],tal[i]);
}
int qr(int x,int y)
{
Base ans;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
ans=mg(ans,SGT::qr(1,dfn[top[x]],dfn[x],1,n)),x=fa[top[x]];
}
if(dep[x]>dep[y]) std::swap(x,y);
return mg(ans,SGT::qr(1,dfn[x],dfn[y],1,n)).qr();
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>q;
for(int i=1;i<=n;i++) std::cin>>a[i];
for(int i=1,u,v;i<n;i++) std::cin>>u>>v,de(u,v),de(v,u);
HLD::d1(1),HLD::d2(1,1);
for(int i=1;i<=n;i++) SGT::md(1,HLD::dfn[i],a[i],1,n);
for(int i=1,u,v;i<=q;i++) std::cin>>u>>v,std::cout<<HLD::qr(u,v)<<'\n';
}

浙公网安备 33010602011771号