首页 > 其他分享 >P1084 [NOIP2012 提高组] 疫情控制

P1084 [NOIP2012 提高组] 疫情控制

时间:2023-12-07 21:24:35浏览次数:41  
标签:ch NOIP2012 疫情 int 城市 ++ P1084 检查点 now

题意:

H 国有 $n $ 个城市,这 \(n\) 个城市用 $ n-1 $ 条双向道路相互连通构成一棵树,$1 $ 号城市是首都,也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。

请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

前言:

一道恶心的贪心题,思路不难想,但实现起来需要注意很多细节。以至于我打完后面部分的代码,就忘了我前面部分写了什么。。。

分析:

显然,对于一个军队,不会存在往下面移动的情况。

考虑二分答案。如何判断 \(x\) 小时是否能控制疫情呢?

对于根首都(根节点)的儿子,我们称为「营地」。

我们把军队分成两类。

  • 对于向上走 \(x\) 小时能够走到营地的点,我们称为「闲置军」。
  • 否则称为「固定军」。

对于固定军,TA 只需要走到深度最小的点即可。

那么唯一的问题,就是要合理地给一些营地分配一个闲置军。

特殊的,如果一个营地能够通过固定军把该营地管理地所有边境城市都控制住的话,那么这种营地就不需要分配闲置军了。

记 \(need_i\) 表示需要闲置军的营地,\(r_i\) 表示闲置军。

对 \(need\) 按照该营地到根节点的距离从小到大排序,对 \(r\) 按照原军队位置到目前暂存的营地走的距离从小到大排序。

用个双指针随便贪心即可。不过要注意需要特判闲置军在本身子树的情况。

代码:

#include<bits/stdc++.h>
#define int long long
#define N 50004 
using namespace std;
int read() {
	char ch = getchar(); int x = 0, f = 1;
	while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * f;
}
void write(int x) {
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) write(x / 10);
	putchar('0' + x % 10);
}
int n, d[N]; //d[i]表示i到根节点的距离
int m, s[N], siz[N], dfn[N], cnt, c[N];
int fa[N][21], P[N], tot, G[N], yy[N], son[N];
vector<int>h[N];
struct edge{
	int to, w;
};
vector<edge>p[N];
bool lst[N];
bool vis[N];
int PP[N], kk[N];
void dfs(int x, int father) {
	fa[x][0] = father;
	dfn[x] = ++cnt;
	kk[cnt] = x;
	siz[x] = 1;
	int o = 0;
	for(int i = 0; i < p[x].size(); i++) {
		int y = p[x][i].to;
		if(y == father) continue;
		son[x]++;
		o++;
		d[y] = d[x] + p[x][i].w;
		yy[y] = p[x][i].w;
		if(x == 1) G[y] = y, vis[y] = 1;
		else G[y] = G[x];
		dfs(y, x);
		siz[x] += siz[y];
		PP[x] += PP[y];
	}
	if(o == 0) P[++tot] = x, lst[x] = 1, PP[x] = 1; 
}
struct ljm{
	int v, w;
}r[N]; //r[i]表示到根节点已经花费的步数 
int len; 
bool cmp(ljm x, ljm y) {
	return x.w < y.w;
}
bool can[N]; //can[i]表示i这个子树的儿子已经铺好了,不用管它 
bool nn[N];
bool check(int x) { //最长移动时间在x内是否可行 
    for(int i = 1; i <= n; i++) c[i] = 0, can[i] = 0, nn[i] = 0;
    len = 0;
	for(int i = 1; i <= m; i++) {
		int now = s[i];
		for(int j = 20; j >= 0; j--) {
			if(fa[now][j] <= 1 || d[s[i]] - d[fa[now][j]] > x) continue;
			now = fa[now][j];
		}
		if(vis[now]) r[++len] = ((ljm){now, d[s[i]]}); //r[i]存资源 
		else {
			c[dfn[now]]++;
		    c[dfn[now] + siz[now]]--;
		}
		
	}
	for(int i = 1; i <= n; i++) c[i] += c[i - 1];
	for(int i = 1; i <= n; i++) 
	if(c[i] >= 1 && lst[kk[i]]) c[i] = 1;
	else c[i] = 0;
	//cout << "c:";
	//for(int i = 1; i <= n; i++) cout << c[i] << " ";
	//cout << endl;
	for(int i = 1; i <= n; i++) c[i] += c[i - 1];
	for(int i = 1; i <= n; i++) {
		if(vis[i]) {
		    if(son[i] == 0) continue;
		    int res = 0;
		    for(auto x : p[i]) {
		    	int X = x.to;
		    	if(X == fa[i][0]) continue;
		    	res += PP[X];
			}
			//cout << i << " : " << res << " " << c[dfn[i] + siz[i] - 1] - c[dfn[i] - 1] << endl;
			if(res == c[dfn[i] + siz[i] - 1] - c[dfn[i]]) can[i] = 1;
		}
	}
	vector<ljm>need; //需要帮助的子树 
	for(int i = 1; i <= n; i++) {
		if(vis[i] == 1 && can[i] == 0) need.push_back((ljm){i, yy[i]});  
	}
	vector<int>G[N];
	sort(r + 1, r + len + 1, cmp);
	for(int i = 1; i <= len; i++) {
		G[r[i].v].push_back(i);
	}
	sort(need.begin(), need.end(), cmp);
	//cout << "e";
	//for(auto now : need) cout << now.v << " ";
    //cout << endl;
	/*cout << "oooo";
    for(auto now : need) cout << now.v << " ";
    cout << endl;
    for(int i = 1; i <= len; i++) cout << r[i].v << " ";
    cout << endl;*/
	int can = len;
	for(auto now : need) {
		int Get = -1;
		while(can) {
			if(nn[can] == 1) can--;
			else if(r[can].w + now.w <= x) {
				Get = can; 
				break;
			}
			else can--;
		}
		//cout << "e" << can << endl;
		int o = -1, Max = 0;
		for(auto y : G[now.v]) {
			if(nn[y]) continue;
			if(r[y].w - yy[now.v] <= x) {
				if(r[y].w > Max) {
					Max = r[y].w;
					o = y;
				}
			}
		}
		if(o == -1 && Get == -1) { //两个都找不到合适的 
			return 0;
		}
		if(o == -1) {
			nn[Get] = 1;
		} 
		else if(Get == -1) {
			nn[o] = 1;
		}
		else if(Max > r[Get].w) {
			nn[o] = 1;
		}
		else nn[Get] = 1;
	}
	return 1;
}
signed main() {
    n = read();
    for(int i = 1; i <= n - 1; i++) {
    	int u = read(), v = read(), w = read();
		p[u].push_back((edge){v, w});
		p[v].push_back((edge){u, w});
	}
	m = read();
	for(int i = 1; i <= m; i++) s[i] = read();
	
	dfs(1, 0);
	//cout << "PP" << PP[7] << endl;
	for(int j = 1; j <= 20; j++) 
		for(int i = 1; i <= n; i++) fa[i][j] = fa[fa[i][j - 1]][j - 1];
	int L = 0, R = 1e14, mid, ans = -1;
	while(L <= R) {
		mid = (L + R) / 2;
		if(check(mid)) {
			ans = mid;
			R = mid - 1;
		}
		else L = mid + 1;
	}
	write(ans);
	//cout << check(9);
	return 0;
}
/*
4 
1 2 1 
1 3 2 
3 4 3 
2 
2 2
*/

/*
8
4 1 2
3 4 8
2 1 7
7 8 8
8 4 3
6 1 7
4 5 1
7
6 7 5 6 6 6 6
*/

标签:ch,NOIP2012,疫情,int,城市,++,P1084,检查点,now
From: https://www.cnblogs.com/xcs123/p/17883988.html

相关文章

  • P1084 [NOIP2012 提高组] 疫情控制
    首先军队可以原地不动,时间越多越容易合法,先套上二分。在不回到根的情况下,军队深度肯定越小越好。所以军队能往上移就移,如果能回到根就暂时在根对应的儿子那里驻扎。这个过程用树上倍增优化。做完这一步后,我们找出需要军队驻扎的根的儿子(向下不经过军队就能到达叶子),现在就是要让......
  • P1081 [NOIP2012 提高组] 开车旅行
    题目有点长,一步一步来。预处理出每座城市两人分别会选择的下一座城市用set即可实现。倍增优化DP令\(f_{i,j}\)表示从城市\(j\)出发,行驶\(2^i\)天会到达的城市。令\(ga_{i,j}\)表示从城市\(j\)出发,行驶\(2^i\)天,小A行驶的路程。\(gb_{i,j}\)同理。答案枚......
  • 基于Springboot的小区疫情购物系统-计算机毕业设计源码+LW文档
    摘 要信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古以来的短板,有效的提升管理的效率和业务水平。传统的管理模式,时间越久管理的内容......
  • 新冠肺炎疫情可视化系统-计算机毕业设计源码+LW文档
    开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:PyCharm浏览器:谷歌浏览器DROPTABLEIFEXISTSaboutus;/*!40101SET@saved_cs_client=@@character_set_client/;/!40101SETcharacter_set_client=utf8......
  • [NOIP2012 提高组] 开车旅行
    题目描述小AA和小BB决定利用假期外出旅行,他们将想去的城市从11到nn编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市ii的海拔高度为hihi​,城市ii和城市jj之间的距离di,jdi,j​恰好是这两个城市海拔高度之差的绝对值,即di,j=∣h......
  • 小红书疫情时代消费心理研究--小红书研究院
    https://kdocs.cn/l/cc0b1U9MpYsc报告阅读说明研究对象:过去一年内在美妆、服饰、母婴、家电/数码、家居/家装、食品饮料、奢侈品品类中有过消费或目前拥有汽车产品的小红书用户;样本覆盖国内1-6线城市,总有效样本共2036个。研究数据:本次报告基于2023年1月收集的调研数据,涉及与《20......
  • P1084疫情控制 题解
    P1084疫情控制前言:这题思路不难,实现稍微有点难。总体来说,不算特别难的那种紫题,建议评蓝。题目描述给定一些点,用这些点来切断根节点到所有叶子节点的路径,可以移动这些点,不同的点可以同时移动,求时间最少。思考过程不同的点可以同时移动:看到这里,我们可以转化一下题目:给定......
  • 后疫情时代,制造企业建设数字化智能工厂的必须技术需求:APS高级计划排程系统
       近年来,中国经济受到了许多因素的影响,例如新冠疫情冲击和国内外经济环境的巨大变化,随着我国人口红利的减少和人力成本逐步的增加,不论是中大型或小微制造企业为了提高市场竞争力并降低生产成本,都纷纷开始规划建设数字化智能工厂。在此背景下,APS高级计划排程系统成为了中国......
  • 疫情控制
    P1084[NOIP2012提高组]疫情控制我们先考虑允许走到根的做法。首先就是二分答案,然后每个军队尽可能往上跳跃,可以用倍增。(往下不优),最后检查是不是满足要求就行了。不允许到根,所以可能有的军队需要支援其他地方。我们先把不能到达根的点先原地驻扎。此时,我们发现对于一个军队......
  • P1075 [NOIP2012 普及组] 质因数分解
    因为n是两个质数的乘积,所以直接暴力枚举,只要能被整除,直接输出因为是要求大的那个,所以从小到大枚举,输出商即可点击查看代码#include<bits/stdc++.h>usingnamespacestd;#defineLLlonglongintmain(){ LLn; cin>>n; for(inti=2;1LL*i*i<=n;i++){ if......