[bzoj2286][Sdoi 2011]消耗战

时间:2021-06-01 16:18:09

[bzoj2286]消耗战

标签: 虚树 DP


题目链接

题解

很容易找出\(O(mn)\)的做法。

只需要每次都dp一遍。

但是m和n是同阶的,所以这样肯定会T的。

注意到dp的时候有很多节点是不需要的,真正有用的只是被询问的那k个点和他们的lca。

所以对于每次询问,我们只需要对于其中的点建一颗虚树。

然后在虚数上面dp。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
#define EREP_g(i,a) for(int i=start_g[(a)];i;i=g[i].next)
inline int read()
{
int sum=0,p=1;char ch=getchar();
while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
if(ch=='-')p=-1,ch=getchar();
while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum*p;
} const int maxn=250020; struct node {
int v,next;ll w;
}; struct Node {
int v,next;
}; const ll inf=1ll<<60;
node e[maxn*2];Node g[maxn*2];
int cnt,start[maxn],cnt_g,start_g[maxn]; inline void addedge(int u,int v,ll w)
{
e[++cnt]=(node){v,start[u],w};
start[u]=cnt;
} inline void addedge_g(int u,int v)
{
g[++cnt_g]=(Node){v,start_g[u]};
start_g[u]=cnt_g;
} int n,dfn[maxn],times,deep[maxn],p[maxn][20];
ll dp[maxn],val[maxn]; inline void dfs(int u,int fa)
{
dfn[u]=++times;
deep[u]=deep[fa]+1;
p[u][0]=fa;
EREP(i,u)
{
int v=e[i].v;
if(v==fa)continue;
val[v]=min(val[u],e[i].w);
dfs(v,u);
} } inline void pre_LCA()
{
for(int j=1;(1<<j)<=n;j++)
REP(i,1,n)p[i][j]=p[p[i][j-1]][j-1];
} inline int lca(int u,int v)
{
if(deep[u]<deep[v])swap(u,v);
DREP(i,19,0)if(deep[p[u][i]]>=deep[v])u=p[u][i];
if(u==v)return v;
DREP(i,19,0)if(p[u][i]!=p[v][i])u=p[u][i],v=p[v][i];
return p[u][0];
} inline void init()
{
n=read();
REP(i,1,n-1)
{
int u=read(),v=read();ll W;scanf("%lld",&W);
addedge(u,v,W);
addedge(v,u,W);
//w[v]=W;
}
val[1]=inf;
dfs(1,0);
pre_LCA();
} inline bool cmp(const int a,const int b)
{
return dfn[a]<dfn[b];
} inline void Dp(int u,int fa)
{
ll res=0;dp[u]=val[u];
EREP_g(i,u)
{
int v=g[i].v;
if(v==fa)continue;
Dp(v,u);
res+=dp[v];
}
start_g[u]=0;
if(res)dp[u]=min(res,dp[u]);
} int add[maxn],st[maxn];
inline void doing()
{
REP(i,1,read())
{
int top=0,tot=1;
int k=read();
REP(j,1,k)add[j]=read();
cnt_g=0;//REP(j,1,2*k)start_g[j]=0;
sort(add+1,add+k+1,cmp);
REP(i,2,k)if(lca(add[tot],add[i])!=add[tot])add[++tot]=add[i];
st[++top]=1;k=tot;
REP(j,1,k)
{
int u=st[top],v=add[j],Lca=lca(u,v);
if(u!=Lca)
{
//int x=st[--top];
while(dfn[st[--top]]>dfn[Lca])addedge_g(st[top],st[top+1]),addedge_g(st[top+1],st[top]); addedge_g(Lca,st[top+1]);addedge_g(st[top+1],Lca);
if(Lca!=st[top])st[++top]=Lca;
}
st[++top]=v;
}
--top;
while(top)addedge_g(st[top],st[top+1]),addedge_g(st[top+1],st[top]),top--;
Dp(1,0);
printf("%lld\n",dp[1]);
}
} int main()
{
init();
doing();
return 0;
}