标签:bzoj2286 sdoi2011 消耗战 lca单调性 虚树
题解:
首先我们考虑每次都做一遍树形DP(树形DP自己脑补去,随便乱搞就过了)。
显然这是TLE无疑的。
所以可以利用LCA单调性构建虚树。
思想:
我们发现每次树形DP有很多点用不到,但是却需要被扫过,让他们见鬼去吧!
实现:
我们只对每次扫的图插入本次询问需要的节点,以及它们的LCA。
这样询问了m个点,虚树就至多只需要2m个点(so quick)。
而插入顺序上不妨利用LCA单调性来把点按dfs度排个序,然后挨个插入单调栈。
同时我们要保证单调栈维护的是一条链,也就是一旦不是链了,我们自然可以求LCA,然后在新图建边,然后出栈,然后balabala~~
这块不妨看代码。
最后的时间复杂度可以理解为O(∑ki)的,也就是50W,但是实现技巧上需要一些预处理,即倍增LCA的nlogn。
但是速度还是很快的。
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 501000 #define LOGN 20 #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3fLL using namespace std; struct KSD { int v,len,next; }e[N]; int head[N],cnt; void add(int u,int v,int len) { cnt++; e[cnt].v=v; e[cnt].len=len; e[cnt].next=head[u]; head[u]=cnt; } int fa[N][LOGN],l[N][LOGN]; int deep[N],pos[N]; void dfs(int x,int p) { int i,v; deep[x]=deep[p]+1; pos[x]=++cnt; for(i=head[x];i;i=e[i].next) { v=e[i].v; if(v==p)continue; fa[v][0]=x; l[v][0]=e[i].len; dfs(v,x); } } void array(int n,int logn=19) { int i,j; for(j=1;j<=logn;j++) for(i=1;i<=n;i++) { fa[i][j]=fa[fa[i][j-1]][j-1]; l[i][j]=min(l[i][j-1],l[fa[i][j-1]][j-1]); } } inline int getlca(int x,int y,int logn=19) { int i; if(deep[x]<deep[y])swap(x,y); for(i=logn;i>=0;i--) if(deep[fa[x][i]]>=deep[y]) x=fa[x][i]; if(x==y)return x; for(i=logn;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } inline long long getmin(int x,int y,int logn=19) { int i,ans=inf; if(deep[x]<deep[y])swap(x,y); for(i=logn;i>=0;i--) if(deep[fa[x][i]]>=deep[y]) { ans=min(ans,l[x][i]); x=fa[x][i]; } if(x==y)return ans; for(i=logn;i>=0;i--) if(fa[x][i]!=fa[y][i]) { ans=min(ans,min(l[x][i],l[y][i])); x=fa[x][i],y=fa[y][i]; } if(x!=y)ans=min(ans,min(l[x][0],l[y][0])); return ans; } struct Graph { struct AKL { int v,next; }e[N]; int head[N],cnt; int vis[N],yyc[N],T; void add(int u,int v) { e[++cnt].v=v; if(vis[u]!=T)vis[u]=T,e[cnt].next=0; else e[cnt].next=head[u]; head[u]=cnt; } void set(int x){yyc[x]=T;} long long f[N]; void dfs(int x) { int i,v; f[x]=0; if(vis[x]==T)for(i=head[x];i;i=e[i].next) { dfs(v=e[i].v); f[x]+=min(getmin(v,x),yyc[v]==T?inf:f[v]); } } }G; int n,q; struct Lux { int x,pos; Lux(int _x=0,int _pos=0):x(_x),pos(_pos){} bool operator < (const Lux &a)const{return pos<a.pos;} }lux[N]; int stk[N],top; int main() { // freopen("test.in","r",stdin); int i,j,k; int a,b,c; scanf("%d",&n); for(i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); add(a,b,c),add(b,a,c); } cnt=0; dfs(1,0); array(n); for(scanf("%d",&q);q--;) { G.T++,G.cnt=0; for(scanf("%d",&n),i=1;i<=n;i++) scanf("%d",&a),lux[i]=Lux(a,pos[a]),G.set(a); sort(lux+1,lux+n+1); stk[top=1]=1; for(i=1;i<=n;i++) { int lca=getlca(stk[top],lux[i].x); while(deep[lca]<deep[stk[top]]) { if(deep[stk[top-1]]<=deep[lca]) { int last=stk[top--]; if(stk[top]!=lca)stk[++top]=lca; G.add(lca,last); break; } G.add(stk[top-1],stk[top]),top--; } if(stk[top]!=lux[i].x)stk[++top]=lux[i].x; } while(top)G.add(stk[top-1],stk[top]),top--; G.dfs(1); printf("%lld\n",G.f[1]); } return 0; }
【BZOJ2286】【SDOI2011】消耗战 LCA单调性(构建虚树)+树形DP
标签:bzoj2286 sdoi2011 消耗战 lca单调性 虚树
原文地址:http://blog.csdn.net/vmurder/article/details/42555643