【传送门:BZOJ2286】
简要题意:
给出一棵根为1的树,树边有边权
有m个询问,每个询问输入k个点的编号,求出在使得这k个点不能与根节点连通的情况下,总共要删掉的边权的总和最小,求出最小总边权
题解:
假如只有一个询问,那么我们可以直接dp
对于树上的点x,转移分为两种情况
1.断开自己与父亲的联系,代价为从根到该节点的最小值
2.不考虑该节点(前提是该节点不是询问点),把子树内的所有询问点都断开的代价
但是如果有m个询问的话,逐个逐个DP的复杂度显然O(mn)会爆炸
有一个地方可以作为突破口的就是Σk<=500000
就要用虚树来做了,详细请左转
而且这题在构造的时候要注意,有两个询问点x,y,y在以x为根的子树内,就不把y建到虚树中,因为我们只要分割了x,y自然就被分割了
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #define Maxn 310000 using namespace std; typedef long long LL; struct node{int x,y,next;LL d;}a[Maxn*2];int len,last[Maxn]; void ins(int x,int y,LL d){a[++len]=(node){x,y,last[x],d};last[x]=len;} vector<int> v[Maxn]; int dfn[Maxn],id,dep[Maxn];LL mn[Maxn]; int f[Maxn][21]; int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[x]-dep[y]>=(1<<i)) x=f[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) { if(dep[x]>=(1<<i)&&f[x][i]!=f[y][i]) { x=f[x][i];y=f[y][i]; } } return f[x][0]; } void dfs(int x,int fa) { dfn[x]=++id; for(int i=1;dep[x]>=(1<<i);i++) f[x][i]=f[f[x][i-1]][i-1]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==f[x][0]) continue; mn[y]=min(mn[x],a[k].d); f[y][0]=x;dep[y]=dep[x]+1; dfs(y,x); } } int p[Maxn]; bool cmp(int x,int y){return dfn[x]<dfn[y];} int sta[Maxn],tp; void add(int x) { if(tp==1){sta[++tp]=x;return ;} int lca=LCA(x,sta[tp]); if(lca==sta[tp]) return ;//因为阻隔了上面的点就能阻隔下面的点 while(tp>1&&dfn[sta[tp-1]]>=dfn[lca]) v[sta[tp-1]].push_back(sta[tp]),tp--; if(lca!=sta[tp]) v[lca].push_back(sta[tp]),sta[tp]=lca; sta[++tp]=x; } LL dp(int x) { if(v[x].size()==0) return mn[x]; LL d=0; for(int i=0;i<v[x].size();i++) { int y=v[x][i]; d+=dp(y); } v[x].clear(); return min(d,mn[x]); } int main() { int n; scanf("%d",&n); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); ins(x,y,d);ins(y,x,d); } memset(mn,63,sizeof(mn)); id=dep[1]=0;dfs(1,0); int m; scanf("%d",&m); while(m--) { int k; scanf("%d",&k); for(int i=1;i<=k;i++) scanf("%d",&p[i]); sort(p+1,p+k+1,cmp); tp=0;sta[++tp]=1; for(int i=1;i<=k;i++) add(p[i]); while(tp!=0) v[sta[tp-1]].push_back(sta[tp]),tp--; printf("%lld ",dp(1)); } return 0; }