[SDOI2011]消耗战
题意:一棵树上给定点集,求到根的最小割。
每次询问只和给定的点集以及它们的LCA有关系,那么把这些点拿出来建立一棵树。再进行树DP。具体操作把所有询问点按照dfn排序,再用一个栈进行维护建树。弹栈的时候并不是每个点都和栈中第二个元素连边,有可能最后一个弹栈点和新的LCA连边。DP转移时需要查询树链的最小边,这里用倍增做。
#include<bits/stdc++.h> #define cmin(a,b) (a>b?a=b:a) using namespace std; typedef long long ll; const int N=5e5+10; const int M=2.5e5+10; int head[N],ver[2*M],edge[2*M],nex[2*M],tot=1; inline void add(int x,int y,int z) { ver[++tot]=y,edge[tot]=z,nex[tot]=head[x],head[x]=tot; } int d[N],f[N][25],Min[N][25],dfn[N],num,_t,n; void dfs(int x){//倍增预处理 dfn[x]=++num; for(int i=head[x];i;i=nex[i]){ int y=ver[i];if(y==f[x][0])continue; d[y]=d[x]+1;f[y][0]=x;Min[y][0]=edge[i]; for(int i=1;i<=_t;++i){ f[y][i]=f[f[y][i-1]][i-1]; Min[y][i]=min(Min[y][i-1],Min[f[y][i-1]][i-1]); } dfs(y); } } int lca(int x,int y) { if(d[x]<d[y])swap(x,y); if(d[x]>d[y]) for(int i=_t; i>=0; --i) if(d[f[x][i]]>=d[y]) x=f[x][i]; if(x==y)return x; for(int i=_t; i>=0; --i) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int minEdge(int x,int y){//x-y最大的边 int res=2e9; if(d[x]<d[y])swap(x,y); if(d[x]>d[y]) for(int i=_t; i>=0; --i) if(d[f[x][i]]>=d[y]) cmin(res,Min[x][i]),x=f[x][i]; if(x==y)return res; for(int i=_t; i>=0; --i) if(f[x][i]!=f[y][i]){ cmin(res,Min[x][i]);cmin(res,Min[y][i]); x=f[x][i];y=f[y][i]; } cmin(res,Min[x][0]);cmin(res,Min[y][0]); return res; } bool cmp(int x,int y){return dfn[x]<dfn[y];} int stk[N],top,fa[N],a[N],in_deg[N],cntq; void build(){//建立虚树 sort(a,a+cntq,cmp);//按dfn排序 stk[top=1]=1; for(int i=0;i<cntq;++i){ int LCA=lca(a[i],stk[top]); while(dfn[stk[top-1]]>=dfn[LCA]){ fa[stk[top]]=stk[top-1],--top; ++in_deg[stk[top]]; } if(dfn[stk[top]]>dfn[LCA])fa[stk[top--]]=LCA,++in_deg[LCA]; if(stk[top]!=LCA)stk[++top]=LCA; stk[++top]=a[i]; } while(top>1){ fa[stk[top]]=stk[top-1],--top; ++in_deg[stk[top]]; } } ll dp[N]; int isq[N],clear[N],cnt;//isq:询问点,clear:等待清空 ll solve(){ queue<int> q; cnt=0; for(int i=0;i<cntq;++i)if(in_deg[a[i]]==0) q.push(a[i]); while(!q.empty()){ int x=q.front();q.pop(); clear[cnt++]=x; if(x==1)break; if(isq[x])dp[fa[x]]+=minEdge(x,fa[x]); else dp[fa[x]]+=min(dp[x],(ll)minEdge(x,fa[x])); if(--in_deg[fa[x]]==0)q.push(fa[x]); } ll ans=dp[1]; for(int i=0;i<cnt;++i)dp[clear[i]]=0;//清空dp数组 for(int i=0;i<cntq;++i)isq[a[i]]=0;//清空询问点标记 return ans; } int main(){ int m,x,y,z; scanf("%d",&n); for(int i=1;i<n;++i){ scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } _t=log(n+0.5)/log(2);//init d[1]=1;//init dfs(1); scanf("%d",&m); while(m--){ scanf("%d",&cntq); for(int i=0;i<cntq;++i)scanf("%d",a+i),isq[a[i]]=1; build(); printf("%lld ",solve()); } return 0; }