题目大意:给定一棵树,边上有边权,m次询问,每次选定一些关键点,求将1号节点与所有关键点都切断所需的最小花销
关键点的总数<=50W
首先我们考虑暴力想法
令f[x]表示切断以x为根的子树中所有关键点的最小花销
g[x]表示x是不是关键点
那么对于x的每个子节点y有f[x]=Σmin(g[y]?INF:f[y],Distance(x,y) )
这样每次暴力做一遍树形DP,时间复杂度是O(n*m)的
现在由于每次询问的点数不一定会达到n的级别,对所有节点进行DFS非常浪费
我们可以将询问的关键点拿出来,单独模拟一次DFS
维护一个栈,栈中的元素形成一条由根节点出发的链,初始栈中只有根节点
将所有关键点按照DFS序排序
每次加入一个节点,求出节点与栈顶的LCA,将栈中所有深度大于LCA的节点全都弹掉
然后将LCA和该节点入栈,注意有些重复的情况要考虑
在这个模拟的DFS过程中顺便把DP做了即可
记得开long long
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 250100 #define INF 0x3f3f3f3fll using namespace std; struct abcd{ int to,f,next; }table[M<<1]; int head[M],tot; int m,n,a[M]; int pos[M],dpt[M],fa[M][20],dis[M][20]; void Add(int x,int y,int z) { table[++tot].to=y; table[tot].f=z; table[tot].next=head[x]; head[x]=tot; } void DFS(int x) { static int cnt=0; int i; pos[x]=++cnt; dpt[x]=dpt[fa[x][0]]+1; for(i=head[x];i;i=table[i].next) if(table[i].to!=fa[x][0]) { fa[table[i].to][0]=x; dis[table[i].to][0]=table[i].f; DFS(table[i].to); } } bool Compare(int x,int y) { return pos[x] < pos[y]; } int LCA(int x,int y) { int j; if(dpt[x]<dpt[y]) swap(x,y); for(j=19;~j;j--) if(dpt[fa[x][j]]>=dpt[y]) x=fa[x][j]; if(x==y) return x; for(j=19;~j;j--) if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j]; return fa[x][0]; } int Distance(int x,int y) { int j,re=0x3f3f3f3f; for(j=19;~j;j--) if(dpt[fa[x][j]]>=dpt[y]) re=min(re,dis[x][j]),x=fa[x][j]; return re; } int main() { int i,j,k,x,y,z; cin>>n; for(i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); Add(x,y,z); Add(y,x,z); } DFS(1); for(j=1;j<=19;j++) for(i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1], dis[i][j]=min(dis[fa[i][j-1]][j-1],dis[i][j-1]); static int g[M],stack[M],top; static long long f[M]; cin>>m; for(i=1;i<=m;i++) { scanf("%d",&k); for(j=1;j<=k;j++) scanf("%d",&a[j]); sort(a+1,a+k+1,Compare); stack[++top]=1; f[1]=0;g[1]=0; for(j=1;j<=k;j++) { int lca=LCA(stack[top],a[j]); while(dpt[stack[top]]>dpt[lca]) { if(dpt[stack[top-1]]<=dpt[lca]) { int temp=min(g[top]?INF:f[top],(long long)Distance(stack[top],lca) ); stack[top--]=0; if(lca!=stack[top]) { stack[++top]=lca; f[top]=0;g[top]=0; } f[top]+=temp; break; } else { f[top-1]+=min(g[top]?INF:f[top],(long long)Distance(stack[top],stack[top-1]) ); stack[top--]=0; } } if(stack[top]!=a[j]) { stack[++top]=a[j]; f[top]=0; } g[top]=1; } while(top>1) { f[top-1]+=min(g[top]?INF:f[top],(long long)Distance(stack[top],stack[top-1]) ); stack[top--]=0; } printf("%lld\n",f[top--]); } return 0; } /* f[x]表示切断以x为根的子树中所有关键点的最小花销 g[x]表示x是不是关键点 f[x]=Σmin(g[table[i].to]?INF:f[table[i].to],Distance(x,table[i].to) ) */
BZOJ 2286 SDOI2011 消耗战 倍增LCA+单调栈
原文地址:http://blog.csdn.net/popoqqq/article/details/42493725