您的位置:首页 > 其它

[SDOI2011][bzoj2286] 消耗战 [虚树+dp]

2018-03-14 20:38 549 查看
[b]题面:[/b]

传送门

[b]思路:[/b]

看到所有询问中的点数总和是十万级别的,就想到用虚树~\(≧▽≦)/~啦

首先,树形dp应该是很明显可以看出来的:

设dp[u]表示以u为根的子树(不包括u)中的宝藏岛全部切断的最小需要值

那么显然dp[u]等于所有dp[v]的和(v是u的儿子)与从根(一号结点)到u的路径上的最小边权之间的最小值

然后dp[1]就是答案了

建出虚树然后dp,$O\left(n\right)$解决

[b]Code:[/b]

/#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define ll long long
using namespace std;
const long long inf=(1ll<<50ll);
inline ll read(){
ll re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
ll n,m,dep[250010],fa[250010],st[250010][20],dfn[250010],clk,minn[250010];
struct graph{
ll first[250010],cnt;
struct edge{
ll to,next,w;
}a[500010];
inline void add(ll u,ll v,ll w){
if(u==v) return;
a[++cnt]=(edge){v,first[u],w};first[u]=cnt;
}
void init(){
cnt=0;
}
}G,g;
void dfs(ll u,ll f){
ll i,v;fa[u]=st[u][0]=f;dfn[u]=++clk;dep[u]=dep[f]+1;
for(i=G.first[u];~i;i=G.a[i].next){
v=G.a[i].to;
if(v==f) continue;
minn[v]=min(minn[u],G.a[i].w);
dfs(v,u);
}
}
void ST(){
ll i,j;
for(j=1;j<=19;j++){
for(i=1;i<=n;i++) st[i][j]=st[st[i][j-1]][j-1];
}
}
ll lca(ll l,ll r){
if(dep[l]>dep[r]) swap(l,r);
ll i;
for(i=19;i>=0;i--) if(dep[st[r][i]]>=dep[l]) r=st[r][i];
if(l==r) return l;
for(i=19;i>=0;i--)
if(st[l][i]!=st[r][i]){
l=st[l][i];
r=st[r][i];
}
return fa[l];
}
ll q[250010],tot,s[250010],top,num,f[250010];
bool cmp(ll l,ll r){
return dfn[l]<dfn[r];
}
void dp(ll u){
ll i,v,tmp=0;
for(i=g.first[u];~i;i=g.a[i].next){
v=g.a[i].to;g.first[u]=g.a[i].next;
dp(v);tmp+=f[v];
}
if(!tmp) f[u]=minn[u];
else f[u]=min(minn[u],tmp);
}
int main(){
ll i,t1,t2,t3,j;
n=read();memset(G.first,-1,sizeof(G.first));
for(i=1;i<n;i++){
t1=read();t2=read();t3=read();
G.add(t1,t2,t3);G.add(t2,t1,t3);
}
minn[1]=inf;
dfs(1,0);ST();
m=read();memset(g.first,-1,sizeof(g.first));
for(i=1;i<=m;i++){
tot=read();//memset(q,0,sizeof(q));
for(j=1;j<=tot;j++) q[j]=read(),assert(q[j]<=n);
sort(q+1,q+tot+1,cmp);g.init();
num=0;q[++num]=q[1];
for(j=2;j<=tot;j++) if(lca(q[j],q[num])!=q[num]) q[++num]=q[j];
s[++top]=1;ll grand;
for(j=1;j<=num;j++){
if(q[j]==0) return *(int*)0;
grand=lca(q[j],s[top]);
while(1){
if(dep[s[top-1]]<=dep[grand]){
g.add(grand,s[top--],0);
if(s[top]!=grand) s[++top]=grand;
break;
}
g.add(s[top-1],s[top],0);top--;
}
if(s[top]!=q[j]) s[++top]=q[j];
}
while(--top) g.add(s[top],s[top+1],0);
dp(1);
printf("%lld\n",f[1]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: