P2495 [SDOI2011] 消耗戰
題意簡述:
在一顆樹上,每次給定\(k\)個關鍵點,求切斷1到這些點的最小花費的總和(最小化總花費)
我們考慮dp:記 \(dp[x]\)為切斷\(dp[x]\)內的所有點到1的路徑的最小花費
顯然我們有兩種切法,一種是對每個子樹下的\(dp[to]\) 求和,另一種是記錄一個\(mincut[x]\),表示1->x這段路上的所有邊中最小的邊權
但這樣的時間複雜度是O(nq)的,顯然不行
那我們該怎麼辦呢?
虛樹閃亮登場:
虛樹,顧名思義是一顆虛構的樹
它只記錄了關鍵節點,在本題中,注意到
$ \sum k_i \leq 5\times 10^5$
所以我們考慮建立一顆樹,上面只包含關鍵點和他們的lca
虛樹的建立:
參考dalao題解:
Link
只要我們建立了虛樹,然後直接對每個虛樹結點進行樹上dp然後我們這題就做完了
記得每次做完之後清空陣列和之前虛樹建的邊!!!
Code
#include<bits/stdc++.h>
#define int long long
const int N=5e5+5;
const int inf=1e17;
const int lg=20;
using namespace std;
int n,m,e1_cnt,e2_cnt,dfn_cnt,top;
int head1[N],head2[N],mincut[N],dfn[N],que[N],tag[N];
int dep[N],st[N];
int f[N][lg+5];
struct Edge{
int to,nxt,w;
}e1[N<<1],e2[N<<1];
void add(int x,int y,int w,Edge e[],int head[],int &cnt)
{
e[++cnt]={y,head[x],w};
head[x]=cnt;
}
void dfs1(int x)
{
dfn[x]=++dfn_cnt;
for(int i=1;i<=lg;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head1[x],to,w;i;i=e1[i].nxt)
{
to=e1[i].to,w=e1[i].w;
if(!dfn[to])
{
dep[to]=dep[x]+1;
mincut[to]=min(mincut[x],w);
f[to][0]=x;
dfs1(to);
}
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=lg;i>=0;i--)
{
if(dep[y]<=dep[f[x][i]])x=f[x][i];
}
if(x==y)return x;
for(int i=lg;~i;i--)
{
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int dfs2(int x)
{
int sum=0,res=inf;
for(int i=head2[x],to;i;i=e2[i].nxt)
{
to=e2[i].to;
sum+=dfs2(to);
}
if(tag[x])res=mincut[x];
else res=min(mincut[x],sum);
tag[x]=0;
head2[x]=0;
return res;
}
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
void solve()
{
int k;
scanf("%lld",&k);
for(int i=1;i<=k;i++)
{
scanf("%lld",&que[i]);
tag[que[i]]=1;
}
sort(que+1,que+1+k,cmp);
top=1;
st[top]=que[1];
for(int i=2;i<=k;i++)
{
int now=que[i];
int lca=LCA(now,st[top]);
while(1)
{
if(dep[lca]>=dep[st[top-1]]) //case1 or case2 or case3
{
if(lca!=st[top])//case2 or case 3
{
add(lca,st[top],0,e2,head2,e2_cnt);
if(lca!=st[top-1])//case2
{
st[top]=lca;//st.pop(top) and st.push(lca)
}
else //case3
{
top--;
}
}
// else : case 1
break;
}
else//case 4
{
add(st[top-1],st[top],0,e2,head2,e2_cnt);
top--;
}
}
st[++top]=now;//case 1 2 3 4
}
while(--top)
{
add(st[top],st[top+1],0,e2,head2,e2_cnt);
}
int ans=dfs2(st[1]);
printf("%lld\n",ans);
e2_cnt=1;
}
void work()
{
mincut[1]=inf;
cin>>n;
for(int i=1,x,y,w;i<n;i++)
{
scanf("%lld%lld%lld",&x,&y,&w);
add(x,y,w,e1,head1,e1_cnt);
add(y,x,w,e1,head1,e1_cnt);
}
dfs1(1);
cin>>m;
for(int i=1;i<=m;i++)
{
solve();
}
}
#undef int
int main()
{
//freopen("P2495.in","r",stdin);//freopen("P2495.out","w",stdout);
work();
}