点分治

分析:

用于解决树上点对之间的问题(也就是链)
每个点被访问\(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;
} 
posted @ 2021-03-23 20:54  wwwsfff  阅读(54)  评论(0)    收藏  举报