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)已经咕了两个月了(可能下一次就是一百年后了),觉得我退化了啊,比去年还菜了啊

    * 生而自由 爱而无畏 *
  • 相关阅读:
    Postman自动化传参实操
    sql语句大全(详细)
    selenium自动化测试-处理iframe
    selenium切换窗口
    selenium中三大窗口切换
    第一个Appium脚本
    Capability配置简介
    APP专项
    Appium环境搭建
    转载——Python Selenium 常用方法总结
  • 原文地址:https://www.cnblogs.com/flyfeather6/p/11342386.html
Copyright © 2011-2022 走看看