题目大意:给定一棵树,边上有边权,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