BZOJ 2286 [Sdoi2011]消耗战

题解:对询问点建立虚树

然后在虚树上Dp

每个点父边边权为这个点到根的边权最小值

一开始学了假的虚树

一开始竟然没想到父边边权可以这样赋

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=500009;
const int inf=2000000000;
typedef long long Lint;

int n,m,T;

int cntedge;
int head[maxn];
int to[maxn],nex[maxn],dist[maxn];
void Addedge(int x,int y,int z){
//	cout<<x<<‘ ‘<<y<<‘ ‘<<z<<endl;
	nex[++cntedge]=head[x];
	to[cntedge]=y;
	dist[cntedge]=z;
	head[x]=cntedge;
}

int dfsclock;
int father[maxn],depth[maxn],minedge[maxn];
int L[maxn],R[maxn];
void Dfs(int now,int fa,int ecost){
	L[now]=++dfsclock;
	father[now]=fa;
	depth[now]=depth[fa]+1;
	minedge[now]=min(minedge[fa],ecost);

	for(int i=head[now];i;i=nex[i]){
		if(to[i]==fa)continue;
		Dfs(to[i],now,dist[i]);
	}
	R[now]=++dfsclock;
}

int f[maxn][20];
void LCAinit(){
	for(int i=1;i<=n;++i)f[i][0]=father[i];
	for(int j=1;j<=19;++j){
		for(int i=1;i<=n;++i){
			f[i][j]=f[f[i][j-1]][j-1];
		}
	}
}
int Getlca(int u,int v){
	if(depth[u]<depth[v])swap(u,v);
	for(int j=19;j>=0;--j){
		if(depth[f[u][j]]>=depth[v])u=f[u][j];
	}
	if(u==v)return u;
	for(int j=19;j>=0;--j){
		if(f[u][j]!=f[v][j]){
			u=f[u][j];v=f[v][j];
		}
	}
	return f[u][0];
}

int h[maxn];
int ene[maxn];
int cmp(const int &rhs1,const int &rhs2){
	return L[rhs1]<L[rhs2];
}

int S[maxn],top;
inline int intree(int x,int y){
	return (L[x]>=L[y])&&(R[x]<=R[y]);
}

Lint g[maxn];
int q[maxn];
void Dp(int x,int fa){
	if(ene[x]){
		q[x]=1;g[x]=inf;return;
	}
	q[x]=g[x]=0;
	for(int i=head[x];i;i=nex[i]){
		if(to[i]==fa)continue;
		Dp(to[i],x);
		q[x]|=q[to[i]];
		if(q[to[i]])g[x]+=min(g[to[i]],dist[i]*1LL);
	}
}

int Xsize;
int a[maxn];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n-1;++i){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		Addedge(x,y,z);
		Addedge(y,x,z);
	}

	minedge[0]=inf;
	Dfs(1,0,inf);
	LCAinit();

	memset(head,0,sizeof(head));
	scanf("%d",&T);
	while(T--){
//		cout<<"begin"<<endl;
		scanf("%d",&n);
		for(int i=1;i<=n;++i){
			scanf("%d",&h[i]);ene[h[i]]=1;
		}
		sort(h+1,h+1+n,cmp);
//		for(int i=1;i<=n;++i)cout<<h[i]<<‘ ‘;
//		cout<<endl;
		cntedge=Xsize=0;
		S[top=1]=1;a[++Xsize]=1;
		for(int i=1;i<=n;++i){
			int lca=Getlca(h[i],S[top]);
			while(!intree(h[i],S[top])){
				if(intree(lca,S[top-1])){
					Addedge(lca,S[top],minedge[S[top]]);--top;break;
				}
				Addedge(S[top-1],S[top],minedge[S[top]]);--top;
			}
			if(S[top]!=lca){
				S[++top]=lca;a[++Xsize]=lca;
			}
			S[++top]=h[i];a[++Xsize]=h[i];
		}
		while(top>1){
			Addedge(S[top-1],S[top],minedge[S[top]]);--top;
		}
		Dp(1,0);
		printf("%lld\n",g[1]);
		for(int i=1;i<=Xsize;++i){
			ene[a[i]]=head[a[i]]=0;
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zzyer/p/8455517.html

时间: 2024-09-28 18:48:25

BZOJ 2286 [Sdoi2011]消耗战的相关文章

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1276  Solved: 445[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

bzoj 2286: [Sdoi2011消耗战

1 #include<cstdio> 2 #include<iostream> 3 #define M 1000009 4 #define N 250009 5 #define ll long long 6 #define inf 1000000000000000000LL 7 #include<algorithm> 8 using namespace std; 9 int n,head[N],next[M],u[M],cnt,fa[N][22],deep[N],m,h

BZOJ 2286: [Sdoi2011消耗战 [DP 虚树]

传送门 题意: 删除价值和最小的边使得$1$号点与$k$个关键点不连通 一个树形DP...但是询问多次,保证总的关键点数为$O(n)$ 先说一下这个$DP$ $f[i]$表示子树$i$中的关键点与$1$不连通的最小价值 如果$i$是关键点则必须删除$i$到$1$的权值最小的边,否则$\sum f[child\ of\ i]$ 学了一下虚树...找不到别的资料啊只有别人的$Blog$ 试验了好多写法 貌似其中有好多带$Bug$的写法 最终定下了现在的版本应该是没大有问题的吧...明天再做两道虚树,

BZOJ 2286 SDOI2011 消耗战 倍增LCA+单调栈

题目大意:给定一棵树,边上有边权,m次询问,每次选定一些关键点,求将1号节点与所有关键点都切断所需的最小花销 关键点的总数<=50W 首先我们考虑暴力想法 令f[x]表示切断以x为根的子树中所有关键点的最小花销 g[x]表示x是不是关键点 那么对于x的每个子节点y有f[x]=Σmin(g[y]?INF:f[y],Distance(x,y) ) 这样每次暴力做一遍树形DP,时间复杂度是O(n*m)的 现在由于每次询问的点数不一定会达到n的级别,对所有节点进行DFS非常浪费 我们可以将询问的关键点拿

[Sdoi2011]消耗战

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3108  Solved: 1115[Submit][Status][Discuss] Description 在 一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经 没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

AC日记——[SDOI2011]消耗战 洛谷 P2495

[SDOI2011]消耗战 思路: 建虚树走树形dp: 代码: #include <bits/stdc++.h> using namespace std; #define INF 1e17 #define maxn 250005 #define ll long long #define maxm (maxn<<1) struct LandType { ll id,key; bool operator<(const LandType pos)const { return key

luogu P2495 [SDOI2011]消耗战

二次联通门 : luogu P2495 [SDOI2011]消耗战 /* luogu P2495 [SDOI2011]消耗战 虚树+树形dp 首先对原图构建出虚树 在建图的时候处理出最小值 转移即可 小技巧 在dp的过程中可以顺便把边表清空 将边结构体封装可方便的建多张图 */ #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #define INF 1

【BZOJ2286】[Sdoi2011]消耗战 虚树

[BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿.由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小. 侦查部门还发现,敌军有