给定一棵树和一个关键点集合,把关键点和其相互的 l c a lca lca构成的类似原树的结构,称为虚树.
(其实就是把一些不需考虑的点忽略).
先把关键点按照 d f s dfs dfs序进行排序,用一个栈维护一个极右链,只需精细地维护这个极右链即可.
对于一个根为1的有根树,初始 s t a [ t o p = 1 ] = 1 sta[top=1]=1 sta[top=1]=1.
之后进行每个点进行如下操作:
这题的构造有所不同的是:如果一个点是另一个点的祖先,那么只有祖先保留即可.
#include
#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
#define pll pair
#define pb push_back
#define IT iterator
#define vi vector
#define TP template
#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;
}
设 m p ( x ) mp(x) mp(x)表示 x x x的最小质因子,那么对于 x > 1 x>1 x>1有边 ( x m p ( x ) , x ) (\dfrac x {mp(x)},x) (mp(x)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!) minx∑i=1nwidis(x,i!). ( n ≤ 1 e 5 ) (n\le 1e5) (n≤1e5).
显然 u u u只能取 i ! i! i!到根(1)的路径上的点,然而 n ! n! n!太大了,我们考虑构建虚数.
由于连边的特殊方式,所以 n ! n! n!在树上的字典序 > ( n − 1 ) ! >(n-1)! >(n−1)!.
我们直接顺序考虑 i ! i! i!即可.
对于 i > 1 i>1 i>1,我们要求出 ( i − 1 ) ! , i ! (i-1)!,i! (i−1)!,i!的 l c a lca lca的深度,可以发现最大深度即为 m a x p r i m e ( i ) ∼ n maxprime(i)\sim n maxprime(i)∼n的所有质因子的指数之和,我们用树状数组维护即可.
设 w [ x ] , f [ x ] w[x],f[x] w[x],f[x]分别表示 子树 w w w总和 以及 子树 w ∗ d e p w*dep w∗dep总和.
dp后考虑二次扫描,如果 w [ 1 ] − w [ y ] − w [ y ] < 0 w[1]-w[y]-w[y]<0 w[1]−w[y]−w[y]<0,则以 y y y计算更优.
#include
#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
#define pll pair
#define pb push_back
#define IT iterator
#define vi vector
#define TP template
#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;
}