zoukankan      html  css  js  c++  java
  • LG5024 保卫王国

    题意

    给定一棵(n)个点的树,每个点的代价为(a_i),每条边至少有一个端点要被选。
    (m)次询问,规定(x)(y)选或不选,求覆盖整棵树的最小权值。

    思路

    考场上拿掉(44pts)就放弃了

    下文中的(u)为子节点。
    这是不是非常显然:
    (dp[x][0/1])表示第(x)个点不选/选时的最小代价

    • (dp[x][0]=dp[x][0]+dp[u][1];)
    • (dp[x][1]=dp[x][1]+min(dp[u][0],dp[u][1]);)

    每次询问时把(dp[x][1-tx])(dp[y][1-ty])变为(INF)(O(n))再做一遍就好

    那么我们发现,这样子做有许多是无用功,其实改变了的只是(x)(y)这条路径。
    进阶来了:发现有一些(B)类的,那么暴力跳链,再维护整棵树除了某棵子树外的答案,加起来就好了。
    定义(ndp[x][0/1])表示第(x)个点不选/选时除了(x)这棵子树外的最小代价(不包括(x)点)

    • (ndp[u][0]=ndp[x][1]+dp[x][1]-min(dp[u][0],dp[u][1]);)
    • (ndp[u][1]=min(ndp[u][0],ndp[x][0]+dp[x][0]-dp[u][1]);)

    接下来考虑瓶颈:跳链的部分如何来优化
    其实是可以倍增的
    (Dp[x][i][p][q])表示从第(x)个点(状态为(p))到(x)(2^i)个父亲(状态为(q))的答案
    注意这个是父亲的子树-(x)的子树的答案,因为这样转移时方便,且最后对(sum_链+dp[x][tx]+dp[y][ty]+ndp[lca][0/1])讨论就好
    转移枚举中间点的情况:
    (Dp[j][i][p][q]=min(Dp[j][i-1][p][0]+Dp[f][i-1][0][q],Dp[j][i-1][p][1]+Dp[f][i-1][1][q]);)
    最后求答案,基本跟倍增求(lca)一样,看代码吧(暴露懒的本性)

    
    #include <bits/stdc++.h>
    using std::min;
    using std::swap;
    const long long INF=10000000000;
    const int N=100005;
    int to[N<<1],edge,last[N],Next[N<<1],a[N],deep[N],n,x,y,tx,ty,m;
    char s[3];
    long long dp[N][2],ndp[N][2],fa[N][21],Dp[N][21][2][2],fx[2][2],fy[2][2];
    void add(int x,int y){
    	to[++edge]=y;
    	Next[edge]=last[x];
    	last[x]=edge;
    }
    void dfs(int x){
    	dp[x][1]=a[x];
    	for (int i=last[x];i;i=Next[i])
    	if (to[i]!=fa[x][0]){
    		int u=to[i];
    		fa[u][0]=x,deep[u]=deep[x]+1;
    		dfs(to[i]);
    		dp[x][0]=dp[x][0]+dp[u][1];
    		dp[x][1]=dp[x][1]+min(dp[u][0],dp[u][1]);
    	}
    }
    void dfs2(int x){
    	for (int i=last[x];i;i=Next[i])
    	if (to[i]!=fa[x][0]){
    		int u=to[i];
    		ndp[u][0]=ndp[x][1]+dp[x][1]-min(dp[u][0],dp[u][1]);
    		ndp[u][1]=min(ndp[u][0],ndp[x][0]+dp[x][0]-dp[u][1]);
    		dfs2(u);
    	}
    }
    void pre(){
    	memset(Dp,0x3f,sizeof(Dp));
    	for (int i=1;i<=n;i++){
    		int f=fa[i][0];
    		Dp[i][0][0][1]=dp[f][1]-min(dp[i][0],dp[i][1]);
    		Dp[i][0][1][0]=dp[f][0]-dp[i][1];
    		Dp[i][0][1][1]=dp[f][1]-min(dp[i][0],dp[i][1]);
    	}
    	for (int i=1;i<=20;i++)
    		for (int j=1;j<=n;j++){
    			int f=fa[j][i-1];
    			fa[j][i]=fa[fa[j][i-1]][i-1];
    			for (int x=0;x<=1;x++)
    				for (int y=0;y<=1;y++)
    					Dp[j][i][x][y]=min(Dp[j][i-1][x][0]+Dp[f][i-1][0][y],Dp[j][i-1][x][1]+Dp[f][i-1][1][y]);
    		}
    } 
    long long solve(int x,int tx,int y,int ty){
    	if (deep[x]<deep[y])
    		swap(x,y),swap(tx,ty);
    	int flag=1;
    	fx[flag][1-tx]=INF,fx[flag][tx]=dp[x][tx];
    	long long sum;
    	for (int i=20;i>=0;i--)
    	if (deep[fa[x][i]]>=deep[y]){
    		int f=fa[x][i];
    		flag=1-flag;
    		fx[flag][0]=min(fx[1-flag][0]+Dp[x][i][0][0],fx[1-flag][1]+Dp[x][i][1][0]);
    		fx[flag][1]=min(fx[1-flag][0]+Dp[x][i][0][1],fx[1-flag][1]+Dp[x][i][1][1]);
    		x=fa[x][i];
    	} 
    	if (x==y) return fx[flag][ty]+ndp[y][ty];
    	fy[flag][1-ty]=INF,fy[flag][ty]=dp[y][ty];
    	for (int i=20;i>=0;i--)
    	if (fa[x][i]!=fa[y][i]){
    		int f=fa[x][i];
    		flag=1-flag;
    		fx[flag][0]=min(fx[1-flag][0]+Dp[x][i][0][0],fx[1-flag][1]+Dp[x][i][1][0]);
    		fx[flag][1]=min(fx[1-flag][0]+Dp[x][i][0][1],fx[1-flag][1]+Dp[x][i][1][1]);
    		x=fa[x][i];
    		f=fa[y][i];
    		fy[flag][0]=min(fy[1-flag][0]+Dp[y][i][0][0],fy[1-flag][1]+Dp[y][i][1][0]);
    		fy[flag][1]=min(fy[1-flag][0]+Dp[y][i][0][1],fy[1-flag][1]+Dp[y][i][1][1]);
    		y=fa[y][i];
    	}
    	int f=fa[x][0];
    	long long sum0=fx[flag][1]+fy[flag][1]+dp[f][0]-dp[x][1]-dp[y][1];
    	long long sum1=min(fx[flag][1],fx[flag][0])+min(fy[flag][0],fy[flag][1])+dp[f][1]-min(dp[x][0],dp[x][1])-min(dp[y][0],dp[y][1]);
    	return min(sum0+ndp[f][0],sum1+ndp[f][1]);
    }
    int main(){
    	scanf("%d%d%",&n,&m);
    	gets(s);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for (int i=1;i<n;i++){
    		scanf("%d%d",&x,&y);
    		add(x,y),add(y,x); 
    	}
    	deep[1]=1;
    	dfs(1);
    	dfs2(1);
    	pre();
    	for (int i=1;i<=m;i++){
    		scanf("%d%d%d%d",&x,&tx,&y,&ty);
    		long long ans=solve(x,tx,y,ty);
    		if (ans>=INF) puts("-1");
    		else printf("%lld
    ",ans);
    	}
    } 
    

    后记

    或许我的(blog)已经咕了两个月了(可能下一次就是一百年后了),觉得我退化了啊,比去年还菜了啊

    * 生而自由 爱而无畏 *
  • 相关阅读:
    MS CRM 2011 RC中的新特性(4)——活动方面之批量编辑、自定义活动
    最近的一些有关MS CRM 2011的更新
    MS CRM 2011 RC中的新特性(6)——连接
    MS CRM 2011 RC中的新特性(7)—仪表板
    参加MS CRM2011深度培训课程——第一天
    MS CRM 2011插件调试工具
    MS CRM2011实体介绍(四)——目标管理方面的实体
    MS CRM 2011 RC中的新特性(3)——客户服务管理方面
    MS CRM 2011 RC中的新特性(8)—数据管理
    ExtAspNet 登陆
  • 原文地址:https://www.cnblogs.com/flyfeather6/p/11342386.html
Copyright © 2011-2022 走看看