[BZOJ2286] [Sdoi2011]消耗战

2024-01-09 12:39
文章标签 sdoi2011 bzoj2286 消耗战

本文主要是介绍[BZOJ2286] [Sdoi2011]消耗战,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

传送门

http://www.lydsy.com/JudgeOnline/problem.php?id=2286

题目大意

给定一棵树,树上有边权,切断一条边消耗边权大小的能量
每次给定一些关键点,使这些关键点都不能与1联通,询问最小代价

题解

树形DP
dp[i]:使i
dp[i]=min{dp[son[i]],w[i,son[i]]}son[i]/w[i,son[i]]son[i]
复杂度 O(NM) 超时
我们发现其实不用每次都对整棵树做一遍 DP ,每次只留下关键点和汇集关键点的点( lca )即可
(下面的父子关系是省略无关点后的新树)
dp[i]=min{dp[son[i]],w[i,son[i]]}son[i]/w[i,son[i]]son[i]
其中 w[i,son[i]] 是原树中两点见的最小边权
可以用ST表预处理出来做到每次 O(logN)
(这一种抛去无关点建立的新树通常叫做虚树)
如何建立,看下面一段

接下来所说的”树”均指虚树,原来那棵树叫做”原树”.
构建过程如下:
按照原树的dfs序号(记为dfn)递增顺序遍历选择的节点. 每次遍历节点都把这个节点插到树上.
首先虚树一定要有一个根. 随便扯一个不会成为询问点的点作根.
维护一个栈,它表示在我们已经(用之前的那些点)构建完毕的虚树上,以最后一个插入的点为端点的DFS链.
设最后插入的点为p(就是栈顶的点),当前遍历到的点为x.我们想把x插入到我们已经构建的树上去.
求出lca(p,x),记为lca.有两种情况:
  1.p和x分立在lca的两棵子树下.
  2.lca是p.
  (为什么lca不能是x?
   因为如果lca是x,说明dfn(lca)=dfn(x)

constmaxn=250000;
varw:array[0..3*maxn,1..3]of longint;dp:array[0..maxn]of int64;pos,dep,t,s,v,p:array[0..maxn]of longint;st:array[0..maxn,0..20]of longint;dis:array[0..maxn,0..20]of int64;i,j,k:longint;n,m,len,a,b,c,tt:longint;
function min(a,b:int64):int64;
begin if a>b then exit(b) else exit(a); end;procedure init(a,b,c:longint);
beginw[len,1]:=b; w[len,2]:=c;if w[a,3]=0 then w[a,3]:=len else w[w[a,1],3]:=len;w[a,1]:=len; inc(len);
end;procedure sort(l,r:longint);
var i,j,a,b:longint;
begini:=l;j:=r;a:=pos[s[(l+r)div 2]];repeatwhile (pos[s[i]]<a) do inc(i);while (pos[s[j]]>a) do dec(j);if not(i>j) then begin b:=s[i]; s[i]:=s[j]; s[j]:=b; inc(i); dec(j); end;until i>j;if l<j then sort(l,j);if i<r then sort(i,r);
end;procedure dfs(a:longint);
var tt:longint;
begintt:=w[a,3]; inc(len); pos[a]:=len;while tt<>0 dobeginif (w[tt,1]<>st[a,0])thenbegindep[w[tt,1]]:=dep[a]+1;st[w[tt,1],0]:=a;dis[w[tt,1],0]:=w[tt,2];dfs(w[tt,1]);end;tt:=w[tt,3];end;
end;procedure prepare;
var i:longint;
beginlen:=0; dep[1]:=1; dfs(1);for i:=1 to 20 dofor j:=1 to n dost[j,i]:=st[st[j,i-1],i-1];for i:=1 to 20 dofor j:=1 to n dodis[j,i]:=min(dis[j,i-1],dis[st[j,i-1],i-1]);
end;function lca(a,b:longint):longint;
var i:longint;
beginif dep[a]<dep[b] then begin i:=a; a:=b; b:=i; end;for i:=20 downto 0 doif dep[st[a,i]]>=dep[b]then a:=st[a,i];if a=b then exit(a);for i:=20 downto 0 doif st[a,i]<>st[b,i]then begin a:=st[a,i]; b:=st[b,i]; end;exit(st[a,0]);
end;function distance(a,b:longint):int64;
var ans:int64; i:longint;
beginif dep[a]<dep[b] then begin i:=a; a:=b; b:=i; end;ans:=maxlongint;for i:=20 downto 0 doif dep[st[a,i]]>=dep[b]then begin ans:=min(ans,dis[a,i]); a:=st[a,i]; end;exit(ans);
end;beginreadln(n); len:=n+1;for i:=1 to n-1 dobeginreadln(a,b,c);init(a,b,c); init(b,a,c);end;prepare;readln(m);for i:=1 to m dobeginread(a);for j:=1 to a dobegin read(s[j]); v[s[j]]:=1; end;sort(1,a);t[1]:=1; t[0]:=1; p[0]:=1; p[1]:=1; len:=n+1;for j:=1 to a dobegintt:=lca(t[t[0]],s[j]);if tt=t[t[0]]then begin inc(t[0]); t[t[0]]:=s[j]; inc(p[0]); p[p[0]]:=s[j]; endelsebeginwhile dep[t[t[0]-1]]>dep[tt] dobeginif v[t[t[0]]]=1then inc(dp[t[t[0]-1]],distance(t[t[0]],t[t[0]-1]))else inc(dp[t[t[0]-1]],min(distance(t[t[0]],t[t[0]-1]),dp[t[t[0]]]));dp[t[t[0]]]:=0; dec(t[0]);end;if v[t[t[0]]]=1then inc(dp[tt],distance(tt,t[t[0]]))else inc(dp[tt],min(distance(tt,t[t[0]]),dp[t[t[0]]]));dp[t[t[0]]]:=0; dec(t[0]);if t[t[0]]<>tt then begin inc(t[0]); t[t[0]]:=tt; inc(p[0]); p[p[0]]:=s[j]; end;inc(t[0]); t[t[0]]:=s[j]; inc(p[0]); p[p[0]]:=s[j];end;end;while t[0]>1 dobeginif v[t[t[0]]]=1then inc(dp[t[t[0]-1]],distance(t[t[0]],t[t[0]-1]))else begin inc(dp[t[t[0]-1]],min(distance(t[t[0]],t[t[0]-1]),dp[t[t[0]]])); dp[t[t[0]]]:=0; end;dec(t[0]);  end;writeln(dp[1]); for j:=1 to p[0] dodp[p[j]]:=0;for j:=1 to a dov[s[j]]:=0;end;
end.

这篇关于[BZOJ2286] [Sdoi2011]消耗战的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/587148

相关文章

树链剖分+线段树【SDOI2011】 bzoj2243 染色

题目大意: 给一棵树,每个节点有一个颜色。写一个程序支持把两个点路径上的所有点染成一个颜色,查询两点之间的色段数量。 解题思路: 树链剖分+线段树 首先它是一颗树,而且是修改和查询两点路径上的颜色,可以想到树链剖分。 查询颜色段数可以用线段树维护区间颜色段数。 这道题涉及到区间合并,所以在线段树和lca的时候需要多记录一些东西,当前区间的最左边的颜色,最右边的颜色,已经求出的区间

虚树+树形dp bzoj2286【Sdoi2011】 消耗战

题目大意: 给定一棵根节点为1的带边权的树。 每次给定一些点,求把这些点与树根断开的最小花费。 题目分析: 我们可以O(n)处理出每个点与根断开的最小花费,即该节点到根的路径上的最小边权。 然后我们对于每一个询问,可以把询问的点打上标记,然后可以O(n)动态规划求解。 但是O(n)的时间复杂度接受不了,而且我们发现每次询问并不会询问所有的点,而且询问的总点数不超过50w。 这样我们可

(Luogu) P2495 [SDOI2011]消耗战 (虚树+动态规划)

虚树入门 题目传送门 虚树的主要思想就是对于一棵树,仅仅保留有用的点,重新构建一棵树。 #include<bits/stdc++.h>#define il inline#define pb push_back#define ms(_data,v) memset(_data,v,sizeof(_data))#define SZ(a) int((a).size())using name

【bzoj2286】【sdoi2011】【消耗战】【虚树+dp】

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

BZOJ 2281 Luogu P2490 [SDOI2011]黑白棋 (博弈论、DP计数)

怎么SDOI2011和SDOI2019的两道题这么像啊。。(虽然并不完全一样) 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2281 (luogu) https://www.luogu.org/problemnew/show/P2490 题解: 博弈论好难啊完全学不来QAQ 题目里应该有个限制,是先手不能左移,后手不

【数据结构 树 树链剖分】luogu_2486 [SDOI2011]染色

题意 求树上路径点颜色的块数,带修改操作。 思路 先把树剖成若干链,用线段树维护区间的块数。 查询统计答案时,当一条链调到另一条链,判断一下这两个端点是否相等(即为同一块)。两个点跳时都这样操作。 当两个点到了同一条链上,需要两个端点都和它们上一次查询到的端点判断(因为在循环中去掉的是上一次和现在的重复,此处是循环结束后特判)。 细节详见代码。 代码 #include <cstdi

Bzoj 2243: [SDOI2011]染色(树链剖分+线段树)

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

【SDOI2011】bzoj2243 染色

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。 请你写一个程序依次完成这m个操作。 Input 第一行包含2个整数n和m,分别表示节点数和操作数; 第二行包含n个正整数表示n个节点的初始颜

【算法竞赛学习笔记】[SDOI2011]染色(路径染色+色段查询)

title : [SDOI2011]染色(路径染色+色段查询) tags : ACM,数据结构 date : 2021-11-16 author : Linno P2486 [SDOI2011]染色 题面 给一棵树,支持两种操作: ①给定u,v,w,将u到v的路径上所有节点染成颜色w。 ②给定u,v,查询u到v路径上颜色段的数量。 树链剖分+线段树 树链剖分将无根树拍平后,用