P1084 [NOIP2012 提高组] 疫情控制(倍增&贪心&二分)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1084 [NOIP2012 提高组] 疫情控制(倍增&贪心&二分)相关的知识,希望对你有一定的参考价值。

P1084 [NOIP2012 提高组] 疫情控制(倍增&贪心&二分)

1.倍增预处理出 f [ i ] [ j ] , d [ i ] [ j ] f[i][j],d[i][j] f[i][j],d[i][j] 即结点 i i i到自己第 2 j 2^j 2j个父亲是谁、距离是多少。

2.二分答案

3.每次二分时,每个结点尽靠近根结点,即向上提,这样是最优的,能控制更多叶子。

4.把不能到达的树根的儿子结点的所有位置标记为驻扎,因此这个点不能再往上走了。

5.把能到达树根的儿子结点的位置多余时间记录。

6.dfs搜一下哪些树根的儿子结点还需要驻扎。

7.对可移动的军队排序,然后不能再回到根节点直接驻扎,因为剩余时间更多的也可以移动那个点,而该节点位置走了之后,需要更多的时间占用。

8.把剩余时间和需要占据的时间进行双指针操作。

9.若能满足所有需要占据的点,则返回真,否则返回假。

code

// Problem: P1084 [NOIP2012 提高组] 疫情控制
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1084
// Memory Limit: 125 MB
// Time Limit: 2000 ms
// Date: 2021-07-14 14:47:24
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=5e4+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int n,m,b[N];
struct edge{
	int to,nt,w;
}e[N<<1];
int h[N],cnt;
void add(int u,int v,int w){
	e[++cnt]={v,h[u],w},h[u]=cnt;
	e[++cnt]={u,h[v],w},h[v]=cnt;
}
int d[N][20],f[N][20];
void dfs(int u,int fa){
	f[u][0]=fa;
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to,w=e[i].w;
		if(f[u][0]==v) continue;
		d[v][0]=w;
		dfs(v,u);
	}
}
bool st[N];
bool dfs(int u){
	bool ok=1;
	if(st[u]) return 1;
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to;
		if(f[u][0]==v) continue;
		ok=0;
		if(!dfs(v)) return 0;
	}
	if(ok) return 0;
	return 1; 
}
PII aa[N];
int bb[N],cc[N];
bool nd[N];
int sta;
bool cmp(PII &a,PII &b){
	return a.se<b.se;
}
bool ck(ll tot){
	mst(st,0);
	mst(aa,0),mst(bb,0);
	mst(cc,0);
	mst(nd,0);
	int ca=0,cb=0,ct=0;
	for(int i=1;i<=m;i++){
		ll tmp=0;int x=b[i];
		for(int j=sta;~j;j--) if(f[x][j]>1&&tmp+d[x][j]<=tot){
			tmp+=d[x][j];
			x=f[x][j];
		}
		if(f[x][0]==1&&tmp+d[x][0]<=tot){
			aa[++ca]=make_pair(x,tot-tmp-d[x][0]);
		}
		else st[x]=1; 
	}
	for(int i=h[1];i;i=e[i].nt)
		if(!dfs(e[i].to)) nd[e[i].to]=1;
	sort(aa+1,aa+ca+1,cmp);
	for(int i=1;i<=ca;i++)
		if(nd[aa[i].fi]&&aa[i].se<d[aa[i].fi][0])
			nd[aa[i].fi]=0;
		else bb[++cb]=aa[i].se;
	for(int i=h[1];i;i=e[i].nt)
		if(nd[e[i].to]) cc[++ct]=d[e[i].to][0];
	int i=1,j=1;
	sort(bb+1,bb+cb+1),sort(cc+1,cc+ct+1);
	while(i<=cb&&j<=ct){
		if(bb[i]>=cc[j]) i++,j++;
		else i++;
	}
	return j>ct;
}
int main(){
	scanf("%d",&n);
	ll l=0,r=0;
	for(int i=1,u,v,w;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);add(u,v,w);r+=w;
	}
	sta=log2(n)+1;
	dfs(1,0);
	for(int j=1;j<=sta;j++)
		for(int i=1;i<=n;i++)
		{
			f[i][j]=f[f[i][j-1]][j-1];
			d[i][j]=d[i][j-1]+d[f[i][j-1]][j-1];
		}
	scanf("%d",&m);
	for(int i=1;i<=m;i++) scanf("%d",&b[i]);
	ll ans=-1;
	while(l<=r){
		int mid=l+r>>1;
		if(ck(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld\\n",ans);
	return 0;
}

以上是关于P1084 [NOIP2012 提高组] 疫情控制(倍增&贪心&二分)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

洛谷 P1084 [NOIP2012 提高组] 疫情控制(二分,倍增,贪心)

[NOIP2012提高组]疫情控制

NOIP2012 洛谷P1084 疫情控制

[NOIP2012 提高组] 疫情控制

刷题总结——疫情控制(NOIP2012提高组)