• P2495 [SDOI2011]消耗战


    虚树(dp)+倍增。

    构建虚树:边权为原树这条链上的最小值。

    状转方程:若子节点为关键点,则此边必断,(dp[x]+=w[x->son])

    否则,可以选择断这条边或在子树中自行解决,(dp[x]+=min{w[x->son],dp[son]})

    链上最小值,可以选择树剖维护,这里使用倍增。

    代码如下,仅供参考:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e5+10;
    int n,m,cnt,a[maxn],tag[maxn];
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    	return x*f;
    }
    int beg[maxn],nex[maxn<<1],to[maxn<<1],w[maxn<<1],e;
    inline void add(int x,int y,int z){
    	e++;nex[e]=beg[x];
    	beg[x]=e;to[e]=y;w[e]=z;
    }
    int dfn[maxn],dep[maxn],f[maxn][20],val[maxn][20];
    inline void dfs(int x,int fa,int pri){
    	dfn[x]=++cnt;dep[x]=dep[fa]+1;
    	f[x][0]=fa;val[x][0]=pri;
    	for(int i=1;i<=19;i++){
    		f[x][i]=f[f[x][i-1]][i-1];
    		val[x][i]=min(val[x][i-1],val[f[x][i-1]][i-1]);
    	}
    	for(int i=beg[x];i;i=nex[i])
    		if(to[i]!=fa)dfs(to[i],x,w[i]);
    }
    inline int lca(int x,int y){
    	if(dep[x]<dep[y])swap(x,y);
    	for(int i=19;i>=0;i--)
    		if(dep[f[x][i]]>=dep[y])x=f[x][i];
    	if(x==y)return x;
    	for(int i=19;i>=0;i--)
    		if(f[x][i]!=f[y][i]){
    			x=f[x][i];
    			y=f[y][i];
    		}
    	return f[x][0];
    }
    inline int calc(int x,int y){
    	int res=1e9;swap(x,y);
    	for(int i=19;i>=0;i--)
    		if(dep[f[x][i]]>=dep[y]){
    			res=min(res,val[x][i]);
    			x=f[x][i];
    		}
    	return res;
    }
    long long dp[maxn];int siz[maxn];
    inline void solve(int x){
    	dp[x]=0;siz[x]=tag[x];
    	for(int i=beg[x];i;i=nex[i]){
    		int t=to[i];solve(t);
    		siz[x]+=siz[t];
    		if(tag[t])dp[x]+=w[i];
    		else if(siz[t])dp[x]+=min(dp[t],1ll*w[i]);
    	}
    	if(!beg[x])dp[x]=1e9;
    	//printf("%d %d
    ",x,dp[x]);
    }
    int st[maxn],top;
    inline int cmp(int x,int y){return dfn[x]<dfn[y];}
    int main(){
    	n=read();
    	int x,y,z;
    	for(int i=1;i<n;i++){
    		x=read(),y=read(),z=read();
    		add(x,y,z),add(y,x,z);
    	}
    	memset(val,0x3f,sizeof(val));
    	dfs(1,0,0);
    	m=read();
    	memset(beg,0,sizeof(beg)),e=0;
    	for(int T=1;T<=m;T++){
    		cnt=read();e=top=0;
    		for(int i=1;i<=cnt;i++)
    			a[i]=read();
    		for(int i=1;i<=cnt;i++)
    			tag[a[i]]=1;
    		sort(a+1,a+1+cnt,cmp);
    		st[++top]=1;beg[1]=0;
    		for(int i=1;i<=cnt;i++){
    			if(a[i]==1)continue;
    			int anc=lca(a[i],st[top]);
    			if(anc!=st[top]){
    				while(top>1&&dep[st[top-1]]>dep[anc])
    					add(st[top-1],st[top],calc(st[top-1],st[top])),top--;
    				if(anc!=st[top-1])beg[anc]=0,add(anc,st[top],calc(anc,st[top])),st[top]=anc;
    				else add(anc,st[top],calc(anc,st[top])),top--;
    			}
    			beg[a[i]]=0;st[++top]=a[i];
    		}
    		for(int i=1;i<top;i++)
    			add(st[i],st[i+1],calc(st[i],st[i+1]));
    		solve(1);printf("%lld
    ",dp[1]);
    		for(int i=1;i<=cnt;i++)
    			tag[a[i]]=0;
    	}
    	return 0;
    }
    

    深深地感到自己的弱小。

  • 相关阅读:
    java开发命名规范
    谈谈自己对java的学习看法
    eclipse菜单解释及中英对照《二》
    eclipse菜单解释及中英对照
    Java开发和运行环境的搭建
    详解Java的MyBatis框架中SQL语句映射部分的编写
    常用的几种编程语言的介绍
    CSS中margin和padding的区别
    同一网页间信息的随意切换
    彩票走势图模板
  • 原文地址:https://www.cnblogs.com/syzf2222/p/13888646.html
Copyright © 2020-2023  润新知