失踪人口回归
题目大意:一棵树,切断若干条边使得有资源的点与1(根)不联通,做m次。
题解:
裸dp挺好想,但
O(nm)
的复杂度承受不起。但是可以发现
Σki<500000
,而且每次操作中的许多节点都是没用的,有用的只有
hi
和他们的lca。所以我们就可以通过每次把有用的点建成一棵虚树来跑,这样的话复杂度就小得多了。
关于虚树的构建,参考https://blog.sengxian.com/algorithms/virtual-tree
还有一点,假如几个资源点在同一条链上,只用管最上面的就可以了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,num=0,fst[250010],m,nn=0,a[250010],sta[250010],tp;
long long f[250010],inf=1LL<<50;//inf开小(1<<30)会错。。
struct edge
{
int x,y,c,n;
}e[500010];
struct pnt
{
int fa[20],dep,id;
long long mn;
}p[250010];
void ins(int x,int y,int c)
{
e[++num]={x,y,c,fst[x]};
fst[x]=num;
}
void sd(int x,int fa)
{
p[x].fa[0]=fa;
p[x].dep=p[fa].dep+1;
p[x].id=++nn;
for(int i=1;(1<<i)<=p[x].dep;i++)
p[x].fa[i]=p[p[x].fa[i-1]].fa[i-1];
for(int i=fst[x];i;i=e[i].n)
{
int y=e[i].y;
if(y==fa)
continue;
p[y].mn=min(p[x].mn,(long long)e[i].c);
sd(y,x);
}
}
int lca(int x,int y)
{
if(p[x].dep<p[y].dep)
swap(x,y);
for(int i=19;i>=0;i--)
if((1<<i)<=p[x].dep-p[y].dep)
x=p[x].fa[i];
if(x==y)
return x;
for(int i=19;i>=0;i--)
if((1<<i)<=p[x].dep&&p[x].fa[i]!=p[y].fa[i])
{
x=p[x].fa[i];
y=p[y].fa[i];
}
return p[x].fa[0];
}
int cmp(int x,int y)
{
return p[x].id<p[y].id;
}
void dfs(int x)
{
// printf("%d\n",x);
f[x]=p[x].mn;
long long s=0;
for(int i=fst[x];i;i=e[i].n)
{
int y=e[i].y;
dfs(y);
s+=f[y];
}
// printf("%d %lld %lld\n",x,f[x],s);
if(fst[x])
f[x]=min(f[x],s);
fst[x]=0;
}
void wk()
{
num=0;
f[1]=inf;
int k,kk=0;
scanf("%d",&k);
for(int i=0;i<k;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+k,cmp);/*
for(int i=0;i<k;i++)
printf("%d %d\n",a[i],p[a[i]].id);*/
for(int i=1;i<k;i++)
{
int hh=lca(a[kk],a[i]);
if(hh!=a[kk])
a[++kk]=a[i];
}
sta[tp=1]=1;
for(int i=0;i<=kk;i++)
{
// puts("f");
int z=lca(sta[tp],a[i]);
if(z==sta[tp])
sta[++tp]=a[i];
else
{
// printf("*%d %d %d\n",sta[tp],sta[tp-1],z);
while(tp>=2&&p[sta[tp-1]].dep>=p[z].dep)
{
// puts("f");
ins(sta[tp-1],sta[tp],1);
tp--;
}
if(z!=sta[tp])
{
ins(z,sta[tp--],1);
sta[++tp]=z;
}
sta[++tp]=a[i];
}
}
// puts("f");
for(int i=1;i<tp;i++)
ins(sta[i],sta[i+1],1);
/*
for(int i=1;i<=num;i++)
printf("%d %d\n",e[i].x,e[i].y);*/
dfs(1);/*
for(int i=0;i<=kk;i++)
printf("%d ",f[a[i]]);
puts("");*/
printf("%lld\n",f[1]);
}
int main()
{
memset(fst,0,sizeof(fst));
memset(a,0,sizeof(a));
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
ins(x,y,c);
ins(y,x,c);
}
p[1].mn=inf;
p[1].dep=1;
sd(1,0);/*
for(int i=1;i<=n;i++)
printf("%d ",p[i].mn);
puts("");*/
memset(fst,0,sizeof(fst));
scanf("%d",&m);
while(m--)
{
wk();
}
}