• 【bzoj2286】 消耗战


    http://www.lydsy.com/JudgeOnline/problem.php?id=2286 (题目链接)

    一个小小的细节,WA了一天,欲哭无泪了。。

    题意

      给出一个n个节点的带权树,总共m次询问,每次询问给出K个节点标号,求出切断这些节点与1号节点的路径的最少花费。

    solution

      构造虚数+树形dp。

      首先,有关虚树的题有一个特征,就是题目会给出sigema(k[i])的范围,保证不会太大。所以我们考虑对于每一次询问构造一棵虚树,然后再在虚树上跑dp,可以大大减少复杂度,比如u,v两点之间没有其他询问点,那么我们就可以把uv直接连起来,中间的点是什么我们并不关心。我们只要建出这样一棵树即可:只含当前询问点和它们的lca,并且相对位置关系不变。 
    举个例子,比如说选1,5,8,10号节点。 
    这里写图片描述 

      那么构造出来的的虚树就是: 
    这里写图片描述 

      那么如何构造虚树呢?

      我们先对原树dfs一遍,预处理出dfs序(mark[]),将询问点按照dfs序排序,依次插入一个栈,来动态维护一个叫做“最右链”的东西,就是最右边的一条链……这个也不太好说明,最好自己看着代码模拟一遍……

      用虚树还要满足一个条件,就是要维护的信息。例如,和,最大,最小,都有类似于前缀和的性质。就是我们可以从v(u的后代)直接求出u的答案,而不需要遍历u到v的所有边,否则虚树就没有降低复杂度,因为每次还是要在原树上走。在这道题中,我们用一个数组mn[i]来维护节点i到根节点1的路径上的花费最小的那一条边权,mn[i]我们可以在dfs时预处理出。这有什么用呢,看下面。

      关于如何在虚树上dp,我么有了mn[]数组后,就变的很简单,f[i]表示断开询问点i以及i的子树上的询问点到根节点1的路径的最小花费。转移方程:f[i]=min(mn[i],sigema(f[e[i].to]),其中e[i].to指的是i节点的孩子节点。

      可是我们考虑一种情况,借用上面的图1,若询问点是2和8,mn[8]=4->8=1,mn[2]=1->2=4,按照我们的算法,那么得出的答案就会是1,而这样是不正确的。 

      也就是说,当存在询问点u,v设deep[u]<deep[v]使lca(u,v)==u时,我们的dp方程是不成立的。对于这种情况,选择切断深度浅的点的mn[u]是最优的。想一想,若切断了mn[u],那么在i的子树上的询问点v自然也被切断了。

      所以我们在构建虚树的时候,就预先将这种情况处理掉,也就是在u,v中只选择u放入虚树中。所以虚树中只有叶子节点是询问点。

    细节

      题目数据范围有误→_→。最后输出的答案可能会很大,记得开LL。inf要开到很大,2147483647是远远不够的(博主就这样WA了一天= =)。还有邻接表头head[]不能直接memset。

    代码

    // bzoj2286
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define inf (1ll<<60)
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=300010;
    int a[maxn],deep[maxn],head[maxn],dfn[maxn],fa[maxn][30],bin[30],s[maxn];
    int n,cnt,Q,K;
    LL f[maxn],mn[maxn];
    struct edge {int to,next;LL w;}e[maxn<<1];
    
    bool cmp(int a,int b) {
    	return dfn[a]<dfn[b];
    }
    void link(int u,int v,LL w) {
    	e[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    	e[++cnt]=(edge){u,head[v],w};head[v]=cnt;
    }
    void link(int u,int v) {
    	e[++cnt]=(edge){v,head[u],0};head[u]=cnt;
    }
    void dfs(int x) {
    	dfn[x]=++cnt;
    	for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    	for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
    			mn[e[i].to]=min(mn[x],e[i].w);
    			fa[e[i].to][0]=x;
    			deep[e[i].to]=deep[x]+1;
    			dfs(e[i].to);
    		}
    }
    int lca(int x,int y) {
    	if (deep[x]<deep[y]) swap(x,y);
    	int t=deep[x]-deep[y];
    	for (int i=0;bin[i]<=t;i++) if (bin[i]&t) x=fa[x][i];
    	for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    	return x==y ? x : fa[x][0];
    }
    void build() {
    	cnt=0;int top=1,tot=1;
    	for (int i=2;i<=K;i++) if (lca(a[tot],a[i])!=a[tot]) a[++tot]=a[i];
    	s[top]=1;
    	for (int i=1;i<=tot;i++) {
    		int x;
    		while (top) {
    			x=lca(a[i],s[top]);
    			if (top>1 && deep[x]<deep[s[top-1]]) link(s[top-1],s[top]),top--;
    			else if (deep[x]<deep[s[top]]) {link(x,s[top--]);break;}
    			else break;
    		}
    		if (s[top]!=x) s[++top]=x;
    		s[++top]=a[i];
    	}
    	while (--top) link(s[top],s[top+1]);
    }
    void dp(int x) {
    	LL tmp=0;f[x]=mn[x];
    	for (int i=head[x];i;i=e[i].next) {
    		dp(e[i].to);
    		tmp+=f[e[i].to];
    	}
    	head[x]=0;  //注意只能在这里清空邻接表,不然复杂度不对
    	f[x]=tmp==0 ? mn[x] : min(tmp,mn[x]);
    }
    int main() {
    	bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
    	scanf("%d",&n);
    	for (int u,v,w,i=1;i<n;i++) {
    		scanf("%d%d%d",&u,&v,&w);
    		link(u,v,w);
    	}
    	cnt=0;mn[1]=inf;dfs(1);
    	scanf("%d",&Q);
    	memset(head,0,sizeof(head));
    	for (int i=1;i<=Q;i++) {
    		scanf("%d",&K);
    		for (int j=1;j<=K;j++) scanf("%d",&a[j]);
    		sort(a+1,a+1+K,cmp);
    		build();
    		dp(1);
    		printf("%lld
    ",f[1]);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    Spoj-ANTP Mr. Ant & His Problem
    Spoj-BIPCSMR16 Team Building
    cf575A Fibonotci
    cf660E Different Subsets For All Tuples
    cf468C Hack it!
    cf468B Two Sets
    cf701E Connecting Universities
    Spoj-BLMIRINA Archery Training
    Spoj-BITDIFF Bit Difference
    Spoj-VISIBLEBOX Decreasing Number of Visible Box
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/5916145.html
Copyright © 2020-2023  润新知