2286: [Sdoi2011]消耗战

考虑到删除某个点的最小值就是根到该节点的边的最小值。

然后可以先dfs一遍求出该值,然后考虑对于两个点,删除的价值就是min(dis[x]+dis[y],dis[lca])

所以可以用虚树处理,然后在虚树上dp即可...

c++代码如下:

#include<bits/stdc++.h>
#define rep(i,x,y) for(register int i = x; i <= y; ++ i)
#define repd(i,x,y) for(register int i = x; i >= y; -- i)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T&x)
{
	x = 0;char c;int sign = 1;
	do { c = getchar(); if(c == '-') sign = -1; }while(!isdigit(c));
	do { x = x * 10 + c - '0'; c = getchar(); }while(isdigit(c));
	x *= sign;
}

const int N = 3e5 + 500,M = 1e6+500;const ll inf = 1e18;
struct Edge { int nxt,to,w; }e1[M],e2[M];
inline void add(int&h,Edge&e,int&tot,int x,int y,int w)
{
	if(x == y) return;
	e.w = w;
	e.to = y;
	e.nxt = h;
	h = tot;
}

int n,m,k,sz,d[N],p[N][21],deep[N],idx[N];
int h1[N],h2[N],cnt1,cnt2;
ll dis[N],f[N];int s[N],top;
bool _is[N];

const bool cmp(int x,int y) { return idx[x] < idx[y]; }

void dfs(int x)
{
	idx[x] = ++sz;
	for(register int i = h1[x];i; i = e1[i].nxt)
		if(e1[i].to != p[x][0])
		{
			deep[e1[i].to] = deep[x] + 1;
			p[e1[i].to][0] = x;
			dis[e1[i].to] = min(dis[x],(ll)e1[i].w);
			dfs(e1[i].to);
		}
}

inline int get_lca(int x,int y)
{
	if(deep[x] < deep[y]) swap(x,y);
	repd(i,20,0) if(deep[x] - (1 << i) >= deep[y]) x = p[x][i];
	if(x == y) return x;
	repd(i,20,0)
		if(p[x][i] != p[y][i])
			x = p[x][i],y = p[y][i];
	return p[x][0]; 
}

void DFS(int x,int fa)
{
	f[x] = 0;ll sum = 0;
	for(register int i = h2[x];i;i = e2[i].nxt)
		if(e2[i].to != fa){
			DFS(e2[i].to,x);
			sum += f[e2[i].to];
		}
	if(_is[x]) f[x] = dis[x];
	else f[x] = min(dis[x],sum);
	_is[x] = 0;h2[x] = 0;
} 

int main()
{
	read(n);
	rep(i,2,n)
	{
		int u,v,w;
		read(u); read(v); read(w);
		add(h1[u],e1[++cnt1],cnt1,u,v,w);
		add(h1[v],e1[++cnt1],cnt1,v,u,w);
	}
	
	dis[1] =  inf;
	p[1][0] = 1;
	dfs(1);
	
	rep(j,1,20) rep(i,1,n) p[i][j] = p[p[i][j-1]][j-1];
	
	read(m);
	
	rep(qqq,1,m)
	{
		read(k);
		rep(i,1,k) read(d[i]),_is[d[i]] = 1;
		
		cnt2 = 0; s[top = 1] = 1;
		sort(d + 1,d + 1 + k,cmp);
		
		rep(i,1,k)
		{
			int lca = get_lca(s[top],d[i]);
			while(top > 1 && deep[s[top - 1]] >= deep[lca])
			{
				add(h2[s[top - 1]],e2[++cnt2],cnt2,s[top-1],s[top],0); 
				add(h2[s[top]],e2[++cnt2],cnt2,s[top],s[top-1],0); 
				top --;
			}
			add(h2[lca],e2[++cnt2],cnt2,lca,s[top],0); 
			add(h2[s[top]],e2[++cnt2],cnt2,s[top],lca,0); 
			s[top] = lca;s[++top] = d[i];
		}
		rep(i,2,top)
			add(h2[s[i - 1]],e2[++cnt2],cnt2,s[i-1],s[i],0),
				add(h2[s[i]],e2[++cnt2],cnt2,s[i],s[i-1],0);
		
		DFS(1,1);
		printf("%lld\n",f[1]);
	}
	
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值