[Sdoi2011]消耗战 和 [Heoi2014]大工程

<body> <center><h1>2286: [Sdoi2011]消耗战</h1><span class="green">Time Limit: </span>20 Sec&nbsp;&nbsp;<span class="green">Memory Limit: </span>512 MB<br><span class="green">Submit: </span>6371&nbsp;&nbsp;<span class="green">Solved: </span>2496<br>[<a href="submitpage.php?id=2286">Submit</a>][<a href="problemstatus.php?id=2286">Status</a>][<a href="bbs.php?id=2286">Discuss</a>]</center><h2>Description</h2><div class="content"><div>在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。</div> <div>侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。</div></div><h2>Input</h2><div class="content"><p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">第一行一个整数</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">n</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">,代表岛屿数量。</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'"><o:p></o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接下来</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">n-1</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">行,每行三个整数</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">u,v,w</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">,代表</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">u</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">号岛屿和</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">v</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">号岛屿由一条代价为</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">c</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的桥梁直接相连,保证</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">1&lt;=u,v&lt;=n</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">且</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">1&lt;=c&lt;=100000</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'"><o:p></o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">第</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">n+1</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">行,一个整数</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">m</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">,代表敌方机器能使用的次数。</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'"><o:p></o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接下来</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">m</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">行,每行一个整数</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">k<sub>i</sub></span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">,代表第</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">i</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">次后,有</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">k<sub>i</sub></span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个岛屿资源丰富,接下来</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">k</span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个整数</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'">h<sub>1</sub>,h<sub>2</sub>,…h<sub>k</sub></span><span style="font-size: 12pt; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">,表示资源丰富岛屿的编号。</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'"><o:p></o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman'"><o:p></o:p></span></p></div><h2>Output</h2><div class="content"><p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 12pt; font-family: 宋体;">输出有</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman';">m</span><span style="font-size: 12pt; font-family: 宋体;">行,分别代表每次任务的最小代价。</span><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman';"><o:p></o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US" style="font-size: 12pt; font-family: 'Times New Roman';">&nbsp;</span></p> <p class="NOI" style="margin: 13pt 0cm"><a name="OLE_LINK5"></a><a name="OLE_LINK4"></a><a name="OLE_LINK3"></a><a name="OLE_LINK2"></a></p></div><h2>Sample Input</h2> <div class="content"><span class="sampledata">10<br> 1 5 13<br> 1 9 6<br> 2 1 19<br> 2 4 8<br> 2 3 91<br> 5 6 8<br> 7 5 4<br> 7 8 31<br> 10 7 9<br> 3<br> 2 10 6<br> 4 5 7 8 3<br> 3 9 4 6</span></div><h2>Sample Output</h2> <div class="content"><span class="sampledata">12<br> 32<br> 22</span></div><h2>HINT</h2> <div class="content"><p></p><p>&nbsp;对于100%的数据,2&lt;=n&lt;=250000,m&gt;=1,sigma(ki)&lt;=500000,1&lt;=ki&lt;=n-1</p><p></p></div><h2>Source</h2> <div class="content"><p><a href="problemset.php?search=Stage2 day2">Stage2 day2</a></p></div><center>[<a href="submitpage.php?id=2286">Submit</a>][<a href="problemstatus.php?id=2286">Status</a>][<a href="bbs.php?id=2286">Discuss</a>]</center><br> <a href="./"><span class="red">HOME</span></a> <a href="javascript:history.go(-1)"><span class="red">Back</span></a>
</body>

题解

虚树

虚树即是一颗虚拟构建的一棵树,这个树只包含关键点以及关键lca的点,而其他不影响虚树结构的点和边都相当于进行了路径压缩,整颗虚树的规模不会超过关键点数目的两倍.

对整颗树预处理得到dfs序(即前序遍历),记为dfn[u].

我们使用一个栈,从栈顶到栈底的元素形成虚树的一颗树链,我们就维护这一条链(有点类似笛卡尔树维护右链).

当我们得到一些询问点(关键点)的时候,对这些点按照他们的dfn[u]值进行排序,然后从dfn值小的开始扫描,结合栈中保存的树链信息就可以将这颗虚树构建出来.

假设我们当前扫到的关键点为u,栈指针为top,栈为stk.

  1. 如果栈为空,或者栈中只有一个元素,那么显然应该: stk[++top]=u;
  2. 取lca=LCA(u,stk[top]),如果lca=stk[top],则说明u点应该接着stk[top]点延长当前的树链.做操作: stk[++top]=u;
  3. 如果lca≠stk[top],则说明u与stk[top]分属lca的两颗不同的子树,且包含stk[top]的这颗子树应该已经构建完成了,我们需要做的是:
    将lca的包含stk[top]子树的那部分退栈,并将这部分建边形成虚树.如果lca不在栈(树链)中,那么要把lca也加入栈中,保证虚树的结构不出现问题,随后将u加入栈中,以表延长树链

此题

对于这题建虚树然后简单DP一下就好了。然后这题特别处在于虚树不用建完,因为如果lca必选的话它的子树建出来就没有意义。所以第二种情况可以直接跳出。

时间复杂度\(O(n+\sum k\log n)\),因为使用了树剖求lca。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
typedef pair<int,int> pii;

co int N=5e5+1;
int n,dep[N],fa[N],siz[N],son[N];
ll val[N];
int top[N],pos[N],dfn;
vector<pii> e[N];
void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,::fa[x]=fa,siz[x]=1;
	for(int i=0,y;i<e[x].size();++i){
		if((y=e[x][i].first)==fa) continue;
		val[y]=min(val[x],(ll)e[x][i].second);
		dfs1(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]]) son[x]=y;
	}
}
void dfs2(int x,int top){
	::top[x]=top,pos[x]=++dfn;
	if(!son[x]) return;
	dfs2(son[x],top);
	for(int i=0,y;i<e[x].size();++i)
		if(!::top[y=e[x][i].first]) dfs2(y,y);
}
int lca(int x,int y){
	for(;top[x]!=top[y];x=fa[top[x]])
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
	if(dep[x]>dep[y]) swap(x,y);
	return x;
}

int a[N],s[N],t;
vector<int> ec[N];
il bool cmp(int a,int b){
	return pos[a]<pos[b];
}
void insert(int x){
	if(t==1) return s[++t]=x,void();
	int lca=::lca(x,s[t]);
	if(lca==s[t]) return; // notice
	for(;t>1&&pos[s[t-1]]>=pos[lca];--t) ec[s[t-1]].push_back(s[t]);
	if(lca!=s[t]) ec[lca].push_back(s[t]),s[t]=lca;
	s[++t]=x;
}
ll dp(int x){
	if(ec[x].size()==0) return val[x];
	ll sum=0;
	for(int i=0;i<ec[x].size();++i) sum+=dp(ec[x][i]);
	ec[x].clear();
	return min(sum,val[x]);
}
int main(){
	read(n);
	for(int i=1,u,v,w;i<n;++i){
		read(u),read(v),read(w);
		e[u].push_back(pii(v,w)),e[v].push_back(pii(u,w));
	}
	val[1]=1e18,dfs1(1,0),dfs2(1,1);
	for(int m=read<int>(),k;m--;){
		read(k);
		for(int i=1;i<=k;++i) read(a[i]);
		sort(a+1,a+k+1,cmp);
		s[t=1]=1;
		for(int i=1;i<=k;++i) insert(a[i]);
		for(;t;--t) ec[s[t-1]].push_back(s[t]);
		printf("%lld\n",dp(1));
	}
	return 0;
}
<body>

3611: [Heoi2014]大工程

Time Limit: 60 Sec  Memory Limit: 512 MB
Submit: 2454  Solved: 1096
[Submit][Status][Discuss]

Description

国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。 
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。 
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
 现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
现在对于每个计划,我们想知道:
 1.这些新通道的代价和
 2.这些新通道中代价最小的是多少 
3.这些新通道中代价最大的是多少

Input

第一行 n 表示点数。

 接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
点从 1 开始标号。 接下来一行 q 表示计划数。
对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
 第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。

Output

输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。 

Sample Input

10
2 1
3 2
4 1
5 2
6 4
7 5
8 6
9 7
10 9
5
2
5 4
2
10 4
2
5 2
2
6 1
2
6 1

Sample Output

3 3 3
6 6 6
1 1 1
2 2 2
2 2 2

HINT

n<=1000000 


q<=50000并且保证所有k之和<=2*n 

Source

[Submit][Status][Discuss]

HOME Back
</body>

题解

建立虚树。最大最小值DP非常显然。至于求和有两种办法:

  1. 统计每条边算了多少次
  2. 维护路径长度和sum和siz,每次ans+=sum[u]*siz[v]+siz[u]*(sum[v]+w*siz[v])。

注意只有实际询问的节点统计siz就行了。无聊啊……放一下别人的代码
第一种做法的

void dfs2(int x) {
	siz[x]=bo[x],maxs[x]=0,mins[x]=inf,f[x]=0;
	for (int y=now[x]; y; y=pre[y]) {
		int d=dis[son[y]]-dis[x];
		dfs2(son[y]),siz[x]+=siz[son[y]];
		ans1=min(ans1,mins[x]+mins[son[y]]+d),mins[x]=min(mins[x],mins[son[y]]+d);
		ans2=max(ans2,maxs[x]+maxs[son[y]]+d),maxs[x]=max(maxs[x],maxs[son[y]]+d);
		f[x]+=f[son[y]]+1ll*siz[son[y]]*(num-siz[son[y]])*d;
	}
	if (bo[x]) ans1=min(ans1,mins[x]),ans2=max(ans2,maxs[x]),mins[x]=0;
	now[x]=0;
}

第二种做法的

void dp(int u){
	siz[u]=build[u];
	mx[u]=build[u]?0:-inf;
	mi[u]=build[u]?0:inf;
	sum[u]=0;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		dp(v);
		ans1+=(sum[u]+siz[u]*e[i].v)*siz[v]+sum[v]*siz[u];
		siz[u]+=siz[v];
		sum[u]+=sum[v]+siz[v]*e[i].v;
		ans2=min(ans2,mi[u]+mi[v]+e[i].v);
		ans3=max(ans3,mx[u]+mx[v]+e[i].v);
		mi[u]=min(mi[u],mi[v]+e[i].v);
		mx[u]=max(mx[u],mx[v]+e[i].v);
	}
	head[u]=0;
}

posted on 2019-06-20 08:49  autoint  阅读(143)  评论(0编辑  收藏  举报

导航