虚树笔记

定义

给定一棵树和一个关键点集合,把关键点和其相互的 l c a lca 构成的类似原树的结构,称为虚树.
(其实就是把一些不需考虑的点忽略).

构造

先把关键点按照 d f s dfs 序进行排序,用一个栈维护一个极右链,只需精细地维护这个极右链即可.

对于一个根为1的有根树,初始 s t a [ t o p = 1 ] = 1 sta[top=1]=1 .
之后进行每个点进行如下操作:

  1. 求其与栈顶的 l c a lca .
  2. 每当出现 d e p [ s t a [ t o p 1 ] ] d e p [ l c a ] dep[sta[top-1]]\ge dep[lca] ,那么表明 s t a [ t o p ] sta[top] 不是极右链,此时添加 ( s t a [ t o p 1 ] , s t a [ t o p ] ) (sta[top-1],sta[top]) ,删除栈顶.
  3. 把当前点加入栈顶.

例题

Luogu P2495 [SDOI2011]消耗战

这题的构造有所不同的是:如果一个点是另一个点的祖先,那么只有祖先保留即可.

#include<bits/stdc++.h>
#define x first
#define y second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator 
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=2.5e5+10,size=1<<20,mod=998244353,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

int n,fa[N],dep[N],sz[N],son[N]; ll w[N];
struct edge{int y,next,d;} a[N*2]; int len,last[N];
void ins(int x,int y,int z) {a[++len]=(edge){y,last[x],z}; last[x]=len;}

void dfs1(int x) {
	sz[x]=1; son[x]=0;
	for(int k=last[x],y;k;k=a[k].next) if((y=a[k].y)^fa[x]) {
		fa[y]=x; dep[y]=dep[x]+1;
		w[y]=min(w[x],(ll)a[k].d);
		dfs1(y); sz[x]+=sz[y];
		if(sz[son[x]]<sz[y]) son[x]=y;
	}
}

int Top[N],id[N],tot;
void dfs2(int x,int tp) {
	id[x]=++tot; Top[x]=tp;
	if(!son[x]) return ;
	dfs2(son[x],tp);
	for(int k=last[x],y;k;k=a[k].next) {
		y=a[k].y;
		if(y^fa[x]&&y^son[x]) dfs2(y,y);
	}
}

bool cmp(int x,int y) {return id[x]<id[y];}

int q,k,b[N],sta[N],top;

int lca(int x,int y) {
	while(Top[x]^Top[y]) {
		if(dep[Top[x]]<dep[Top[y]]) swap(x,y);
		x=fa[Top[x]];
	}
	if(dep[x]<dep[y])return x;
	return y;
}

vi e[N];
void add(int x,int y) {e[x].pb(y);}
ll dp(int x) {
	if(!e[x].size()) return w[x];
	ll s=0;
	for(int y:e[x]) s+=dp(y);
	e[x].clear();
	return min(s,w[x]);
}

int main() {
	qr(n);
	for(int i=1,x,y,z;i<n;i++) qr(x),qr(y),qr(z),ins(x,y,z),ins(y,x,z);
	w[1]=1LL<<60; fa[1]=0; dep[1]=1; dfs1(1); dfs2(1,1);
	qr(q); while(q--) {
		qr(k); for(int i=1;i<=k;i++) qr(b[i]);
		sort(b+1,b+k+1,cmp); sta[top=1]=1;
		for(int i=1,x,y;i<=k;i++) {
			x=b[i];
			if(top==1) {sta[++top]=x; continue;}
			y=lca(sta[top],x);
			if(y==sta[top]) continue;
			while(top>1&&dep[sta[top-1]]>=dep[y]) add(sta[top-1],sta[top]),top--;
			if(sta[top]^y) add(y,sta[top]),sta[top]=y;
			sta[++top]=x;
		}
		while(top>1) add(sta[top-1],sta[top]),top--;
		pr2(dp(1)); 
	}
	return 0;
}
	 

Infinite Tree

m p ( x ) mp(x) 表示 x x 的最小质因子,那么对于 x > 1 x>1 有边 ( x m p ( x ) , x ) (\dfrac x {mp(x)},x) .
min x i = 1 n w i d i s ( x , i ! ) \min_x \sum_{i=1}^n w_i dis(x,i!) . ( n 1 e 5 ) (n\le 1e5) .

显然 u u 只能取 i ! i! 到根(1)的路径上的点,然而 n ! n! 太大了,我们考虑构建虚数.
由于连边的特殊方式,所以 n ! n! 在树上的字典序 > ( n 1 ) ! >(n-1)! .
我们直接顺序考虑 i ! i! 即可.

对于 i > 1 i>1 ,我们要求出 ( i 1 ) ! , i ! (i-1)!,i! l c a lca 的深度,可以发现最大深度即为 m a x p r i m e ( i ) n maxprime(i)\sim n 的所有质因子的指数之和,我们用树状数组维护即可.

w [ x ] , f [ x ] w[x],f[x] 分别表示 子树 w w 总和 以及 子树 w d e p w*dep 总和.
dp后考虑二次扫描,如果 w [ 1 ] w [ y ] w [ y ] < 0 w[1]-w[y]-w[y]<0 ,则以 y y 计算更优.

#include<bits/stdc++.h>
#define x first
#define y second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator 
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=2e5+10,size=1<<20,mod=998244353,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

int n;
ll w[N],ans;

int c[N];
void upd(int x,int y) {for( ;x<=n;x+=x&-x) c[x]+=y;}
int ask(int x) {int y=0; for(   ;x;x-=x&-x) y+=c[x]; return y;}

int f[N],g[N],prime[N],tot;//max prime factor
void get(int x) {
    for(int i=2;i<=x;i++) {
        if(!f[i]) {prime[++tot]=f[i]=i;g[i]=1;}
        for(int j=1,k;(k=i*prime[j])<=x;j++) {
            f[k]=f[i]; g[k]=g[i]+1;
            if(i%prime[j]==0) break;
        }
    }
    tot=0;
}

int lcadep[N],dep[N],sta[N],top;
vi e[N];
void add(int x,int y) {e[x].pb(y);}

void bt() {
    sta[top=1]=1; tot=n; dep[1]=0;
    for(int i=2;i<=n;i++) {
        dep[i]=dep[i-1]+g[i];
        lcadep[i]=ask(n)-ask(f[i]-1);
        for(int j=i;j!=1;   ) {
            int k=0,t=f[j];
            while(j%t==0) k++,j/=t;
            upd(t,k);
        }
    }
    for(int i=2;i<=n;i++) {
        while(top>1&&dep[sta[top-1]]>=lcadep[i]) add(sta[top-1],sta[top]),top--;
        if(dep[sta[top]]^lcadep[i]) add(++tot,sta[top]),dep[tot]=lcadep[i],sta[top]=tot;
        sta[++top]=i;
    }
    while(top>1) add(sta[top-1],sta[top]),top--;
}

ll F[N];

void dfs1(int x) {F[x]=w[x]*dep[x]; for(int y:e[x]) dfs1(y),w[x]+=w[y],F[x]+=F[y];}
void dfs2(int x) {
	for(int y:e[x]) 
		if(w[1]-2*w[y]<0) {
			F[y]=F[x]+(dep[y]-dep[x])*(w[1]-2*w[y]); 
			ans=min(ans,F[y]); dfs2(y);
		}
}

int main() {
	get(N-1);
	while(~scanf("%d",&n)) {
		for(int i=1;i<=tot;i++) e[i].clear(),w[i]=c[i]=0;
		tot=0;
		for(int i=1;i<=n;i++) qr(w[i]);
		bt(); dfs1(1); 
		ans=F[1]; dfs2(1); pr2(ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/107917112