点分治
分析:
用于解决树上点对之间的问题(也就是链)
每个点被访问\(lg\)次
例题1
Luogu P4149 [IOI2011]Race
点分治模板题
错因:慌了,找根是找了最大的;每次保证先修改后查询,老老实实,浪费时间浪费脑子
#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;
inline int rd() {
int ret=0; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
return ret;
}
const int N=2e5+5,M=1e6+5;
int n,K,sz[N],mx[N],rt,ans,c[M],d[N];
bool vis[N];
struct A{int v,w; };
vector<A>V[N],V2;
inline void getrt(int fa,int u,int Sz) {
sz[u]=1,mx[u]=0;
for(A v:V[u]) {
if(!vis[v.v]&&v.v!=fa) {
getrt(u,v.v,Sz);
sz[u]+=sz[v.v],mx[u]=max(mx[u],sz[v.v]);
}
}
mx[u]=max(mx[u],Sz-sz[u]);
if(mx[u]<mx[rt]) rt=u;
}
vector<int>V1;
void dgs(int fa,int u,int s) {
if(d[u]>K) return;
ans=min(ans,c[K-d[u]]+s);
V1.push_back(d[u]),V2.push_back((A){d[u],s});
for(A v:V[u]) {
if(!vis[v.v]&&v.v!=fa) {
d[v.v]=d[u]+v.w;
dgs(u,v.v,s+1);
}
}
}
void dfs(int u,int Sz) {
rt=0; getrt(0,u,Sz);
vis[rt]=1; c[d[rt]=0]=0;
V1.push_back(0);
for(A v:V[rt]) {
if(!vis[v.v]) {
V2.clear();
vector<A>().swap(V2);
d[v.v]=d[rt]+v.w;
dgs(rt,v.v,1);
for(A v:V2) {
c[v.v]=min(c[v.v],v.w);
}
}
}
for(int v:V1) c[v]=INF;
V1.clear();
vector<int>().swap(V1);
for(A v:V[rt]) {
if(!vis[v.v]) dfs(v.v,sz[v.v]);
}
}
int main(){
n=rd(),K=rd();
for(int i=1;i<n;i++) {
int u=rd()+1,v=rd()+1,k=rd();
V[u].push_back((A){v,k}),V[v].push_back((A){u,k});
}
ans=INF; mx[0]=INF;
for(int i=1;i<=K;i++) c[i]=INF;
dfs(1,n);
printf("%d\n",ans==INF?-1:ans);
return 0;
}
当然,这儿在找根是应该要二重dfs,我这是错的,但过了,不影响正确性,只影响时间
例题2
Luogu P3292 [SCOI2016]幸运数字
难得的点分治可以比在线快的题
xor需要用线性基维护,每次维护经过根的链,预处理处根到所有点的线性基\(lg\),合并时暴力合并\(lg^2\)
所以时间复杂度是\(O(nlg^2n+qlg^2n)\)
每个点至多访问\(lgn\)次,所以直接把询问丢到节点上即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e4+5,M=2e5+5;
int n,m,sz[N],mx[N],rt,fl[N];
bool vis[N];
ll ans[M],a[N];
vector<int>V[N],V1;
struct B{int v,i; };
vector<B>Q[N];
void getsz(int fa,int u) {
sz[u]=1;
for(int v:V[u]) {
if(!vis[v]&&v!=fa) {
getsz(u,v),sz[u]+=sz[v];
}
}
}
void getrt(int fa,int u,int Sz) {
mx[u]=Sz-sz[u];
for(int v:V[u]) {
if(!vis[v]&&v!=fa) {
getrt(u,v,Sz);
mx[u]=max(mx[u],sz[v]);
}
}
if(!rt||mx[u]<mx[rt]) rt=u;
}
struct A{
ll c[61];
inline void ins(ll x) {
for(int i=60;i>=0;i--) {
if(!x) return;
if(x&(1LL<<i)) {
if(!c[i]) {
c[i]=x; return;
} else x^=c[i];
}
}
}
inline void clear() {
memset(c,0,sizeof(c));
}
inline ll ask() {
ll ret=0;
for(int i=60;i>=0;i--) {
if(c[i]&&(!(ret&(1LL<<i)))) {
ret^=c[i];
}
}
return ret;
}
}c[N],tmp;
void dgs(int fa,int u) {
V1.push_back(u);
for(int v:V[u]) {
if(v!=fa&&!vis[v]) {
c[v]=c[u],c[v].ins(a[v]),fl[v]=fl[u];
dgs(u,v);
}
}
}
void dfs(int u) {
getsz(0,u); rt=0; getrt(0,u,sz[u]);
vis[rt]=1;
// printf("%d %d %d\n",u,rt,sz[u]);
c[rt].ins(a[rt]); V1.push_back(rt);
for(int v:V[rt]) {
if(!vis[v]) {
fl[v]=v,c[v]=c[rt],c[v].ins(a[v]),dgs(0,v);
}
}
fl[rt]=-1;
for(int v:V1) {
// assert(fl[v]>0);
for(B u:Q[v]) {
// printf("%d %d\n",fl[u.v],fl[v]);
if(fl[u.v]&&!ans[u.i]&&fl[u.v]!=fl[v]) {
tmp=c[u.v];
for(int i=0;i<=60;i++) {
if(c[v].c[i]) {
tmp.ins(c[v].c[i]);
}
}
/* for(int i=10;i>=0;i--) {
printf("%d ",tmp.c[i]);
}
puts("");*/
ans[u.i]=tmp.ask();
}
}
}
for(int v:V1) {
c[v].clear(); fl[v]=0;
}
V1.clear();
for(int v:V[rt]) {
if(!vis[v]) {
dfs(v);
}
}
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<n;i++) {
int u,v; scanf("%d%d",&u,&v);
V[u].push_back(v),V[v].push_back(u);
}
for(int i=1;i<=m;i++) {
int u,v; scanf("%d%d",&u,&v);
if(u==v) ans[i]=a[u];
else Q[u].push_back((B){v,i});
}
dfs(1);
for(int i=1;i<=m;i++) {
printf("%lld\n",ans[i]);
}
return 0;
}
例题3
Luogu P2305 [NOI2014] 购票
DP很好想,斜率优化式子很好推
关键在于求\([lim_i,i]\)中的凸包
法1:凸包具有可分割性,最优解具有可加性,所以树状数组维护单调栈维护凸包,用可持久化栈(打标记,暴力回溯)
法2:cdq分治,排序,求解,在树上就变成了点分治
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
const double eps=1e-6;
using namespace std;
ll red()
{
ll ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret;
}
const int N=2e5+5;
int n,he[N],nxt[N],to[N],cnt;
int fa[N],siz[N],nrt,rrt;
int l,r,Q[N],c,b[N];
ll f[N],dep[N],p[N],q[N],L[N],w[N];
bool vis[N];
inline void add(int u,int v,int k)
{
to[++cnt]=v,nxt[cnt]=he[u];
he[u]=cnt,w[cnt]=k;
}
void get_rt(int u,int S)
{
int mx=0; siz[u]=1;
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
if(!vis[v])
{
get_rt(v,S);
siz[u]+=siz[v],mx=max(mx,siz[v]);
}
}
mx=max(mx,S-siz[u]);
if(mx<nrt) nrt=mx,rrt=u;
}
inline double g(int i,int j)
{ return (double)(f[j]-f[i])/(dep[j]-dep[i]); }
void get_b(int u)
{
b[++c]=u;
for(int e=he[u];e;e=nxt[e])
if(!vis[to[e]]) get_b(to[e]);
}
bool cmp(int i,int j){return dep[i]-L[i]>dep[j]-L[j]; }
void solve(int u,int S)
{
rrt=0,nrt=n; get_rt(u,S); int rt=rrt;
vis[rt]=1;
if(S-siz[rt]>1) solve(u,S-siz[rt]);
c=0,get_b(rt),sort(b+1,b+c+1,cmp);
l=n+1,r=n; int v=fa[rt];
for(int i=1;i<=c;i++)
{
while(v!=fa[u]&&dep[v]>=dep[b[i]]-L[b[i]])
{
while(l<r&&g(v,Q[l])+eps>=g(Q[l],Q[l+1])) l++;
Q[--l]=v,v=fa[v];
}
if(l<=r)
{
int _l=l,_r=r-1,ans=r;
while(_l<=_r)
{
int mid=_l+_r>>1;
if(g(Q[mid],Q[mid+1])+eps>p[b[i]]) ans=mid,_r=mid-1;
else _l=mid+1;
}
f[b[i]]=min(f[b[i]],f[Q[ans]]+(dep[b[i]]-dep[Q[ans]])*p[b[i]]+q[b[i]]);
}
}
for(int i=1;i<=c;i++)
if(b[i]!=rt&&dep[b[i]]-L[b[i]]<=dep[rt])
f[b[i]]=min(f[b[i]],f[rt]+(dep[b[i]]-dep[rt])*p[b[i]]+q[b[i]]);
for(int e=he[rt];e;e=nxt[e])
if(!vis[to[e]]&&siz[to[e]]>1) solve(to[e],siz[to[e]]);
}
void get_dep(int u)
{
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
dep[v]=dep[u]+w[e],get_dep(v);
}
}
int main()
{
n=red(); int t=red();
for(int i=2;i<=n;i++)
{
fa[i]=red();int k=red(); add(fa[i],i,k);
p[i]=red(),q[i]=red(),L[i]=red(),f[i]=1e18;
}
get_dep(1),solve(1,n);
for(int i=2;i<=n;i++) printf("%lld\n",f[i]);
return 0;
}

点分治,cdq,斜率优化
浙公网安备 33010602011771号