Connecting...

P1084 [NOIP2012 提高组] 疫情控制 解题报告

题解 P1084 [NOIP2012 提高组] 疫情控制

开始前

这题太TM毒瘤了,太自信以为可以不看题解切,结果越想越不对劲,后来调出来90ptsTLE,就去求救了,最后修正了复杂度。总共写了不知道几个小时

思路

考虑备选时间 \(n_1<n_2\) 如果 \(n_1\) 可以,则 \(n_2\) 肯定可以,则知damn(ans)单调,则对答案二分答案

相对check函数来说,其他的地方如 树上倍增(是这个名字吗?)啥啥啥的显得容易很多,手搓一点hack就能调了。我的程序性能很差,有的地方用极大的空间换了时间。

树上倍增

还是讲一下树上倍增部分吧。

void init(){//肯定没问题 
	for(int j=1;j<20;++j){
		for(int i=2;i<=n;++i){//祖先是谁 到祖先多少时间 
			if(1<=dep[i]-(1<<j)){
				fa[i][j]=fa[fa[i][j-1]][j-1];
				tim[i][j]=tim[i][j-1]+tim[fa[i][j-1]][j-1];
			}
		}
	}
}

\(fa[i][j]\) 表示从i往上jump \(2^j\) 步的祖先,\(fa[i][0]\) 是父节点,惊喜的发现我的 \(fa\) 里不想要的地方都是 \(0\)

\(tim[i][j]\) 类似,从从i往上jump \(2^j\) 步的花费时间,也发现不要的地方为 \(0\)

代码

太感人了,重工业题

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
using ll=long long;
#define pii pair<int,int>
struct army{
	int id,t;
	bool operator<(army y)const{
		return t>y.t;
	}
};
struct Edge{
	int v,nxt;
	ll w;
}edge[maxn*2];
ll sumw=0,w[maxn];
int n,m,head[maxn],edgetot,pos[maxn],pos_[maxn];
ll dep[maxn],fa[maxn][20],tim[maxn][20]/*祖先是谁 到祖先多少时间 */;
int build[maxn],leaf[maxn],leaftot,armytime[maxn],trcolor[maxn]/*某个节点所属的分支*/,colortot,colortoson[maxn]/*某颜色分支所属的 1的子节点*/;
void add(int u,int v,int w){
	edge[++edgetot]=(Edge){v,head[u],w};
	head[u]=edgetot;
}
void dfs1(int u,int f){//初始化
	dep[u]=dep[f]+1;
	bool op=0;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v,w=edge[i].w;
		if(v==f)continue;
		fa[v][0]=u;
		tim[v][0]=w;
		dfs1(v,u);
		op=1;
	}
	if(!op)leaf[u]=++leaftot;
}
void init(){//肯定没问题 
	for(int j=1;j<20;++j){
		for(int i=2;i<=n;++i){//祖先是谁 到祖先多少时间 
			if(1<=dep[i]-(1<<j)){
				fa[i][j]=fa[fa[i][j-1]][j-1];
				tim[i][j]=tim[i][j-1]+tim[fa[i][j-1]][j-1];
			}
		}
	}
}
void dfs2(int u,int f){
	if(build[u])return; 
	build[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v!=f)dfs2(v,u);
	}
}
void dfs3(int u,int f,int c){
	trcolor[u]=c;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v!=f)dfs3(v,u,c);
	}
}
bool cmp(int A,int B){
	return w[A]<w[B];
}
bool cmp1(int A,int B){
	return armytime[A]<armytime[B];
}
priority_queue<army>am[maxn];//每个1的子节点的可移动军队 
priority_queue<army>xz;//闲置暂存 
int q[maxn],qtot=0,node[maxn],nodetot;
int get(int u,int f){//要改 
	int cnt=0;
	if(leaf[u])return (build[u]?0:1);
	if(build[u])return 0;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v==f)continue;
		cnt+=get(v,u);
	}
	return cnt;
}
bool judge(int time){
	nodetot=0,qtot=0,colortot=0,leaftot=0;
	w[0]=INT_MAX;
	for(int i=1;i<=n;++i){//清空 O(n)
		while(am[i].size())am[i].pop();
		build[i]=0;
		armytime[i]=0;
		node[i]=0;
		q[i]=0;
		pos[i]=pos_[i];
	}
	while(xz.size())xz.pop();//O(n)
	for(int i=head[1];i;i=edge[i].nxt){//初始化 O(n)
		int v=edge[i].v;
		w[v]=edge[i].w;
		dfs3(v,1,++colortot);
		colortoson[colortot]=v;
	}
	for(int i=1;i<=m;++i){//O(n+m)
		int f=pos[i],t=time;
		for(int j=19;j>=0;--j){
			if(fa[f][j]!=1&&t>=tim[f][j]&&fa[f][j]!=0){
				t-=tim[f][j],f=fa[f][j];
			}
		}
		if(t>tim[f][0]){//还有多的时间
			armytime[i]=t-tim[f][0];//军队剩余时间 先走到1
			am[f].push({i,armytime[i]});
			f=fa[f][0];
		}else if(!build[f]){//没时间了 最多跳到 1的子节点 
			dfs2(f,fa[f][0]);
		}
		pos[i]=f;//i这个军队的位置
	}
	for(int i=head[1];i;i=edge[i].nxt)//O(n)
	{
		int v=edge[i].v;
		if(am[v].size()&&get(v,1)){//仍未染
			int v_=am[v].top().id;
			if(armytime[v_]<tim[v][0]){//回不去了
				am[v].pop();
				dfs2(v,1);
			}
		}
		while(am[v].size()){
			xz.push(am[v].top());
			am[v].pop();
		}
	}
	for(int i=head[1];i;i=edge[i].nxt){//O(n)
		int v=edge[i].v;
		if(get(v,1))node[++nodetot]=v;
	}
	//剩下的全部是子树染了的、回的去的、总之真正闲置的军队
	sort(node+1,node+1+nodetot,cmp);//按消费从小到大 O(nlogn)
	int nw=0;
	for(int i=1;i<=nodetot;++i){//把染完了的,可以回家的用掉 O(n)
		while(xz.size()&&xz.top().t<w[node[i]])xz.pop();
		if(xz.size()){
			xz.pop();
			dfs2(node[i],1);
		}
	}
	for(int i=2;i<=n;++i)if(leaf[i]&&!build[i])return 0;//O(n)
	return 1;
}
int main(){
//	ios::sync_with_stdio(0),cin.tie(0);
//	freopen("10.in","r",stdin);
//	freopen("10.out","w",stdout);
	cin>>n;
	for(int i=1;i<n;++i){
		int u,v,w;
		cin>>u>>v>>w;
		sumw+=w;
		add(u,v,w);add(v,u,w);
	}
	dfs1(1,0);
	init();
//	for(int i=1;i<=n;++i)for(int j=0;j<=3;++j)cout<<tim[i][j]<<" \n"[j==3];
	cin>>m;
	for(int i=1;i<=m;++i)cin>>pos_[i];
	ll L=0,R=sumw*2,ans=-1;//cout<<judge(3);
	while(L<=R){
		int mid=L+R>>1;
		if(judge(mid))ans=mid,R=mid-1;
		else L=mid+1;
	}
	cout<<ans;
	return 0;
}
/*
6
2 1 7
3 1 5
4 2 1
5 4 1
6 1 1
3
5 6 4 
*///9
/*
5
1 2 3
1 3 2
2 4 2
2 5 3
2
4 5
//7
/* 
		1
	(1)/\(2)
	  2  3
	  	  \(3)
		   4
*/
/*
10
2 1 3
2 3 4
1 4 7
5 1 9
6 1 2
4 7 9
7 8 8
9 8 8
1 10 2
5
2 8 5 4 2 
*/
//9
/*
50
1 2 7528
3 2 3807
3 4 4184
5 2 5932
5 6 6827
7 4 392
8 5 5041
7 9 2394
6 10 999
1 11 4033
12 9 6303
13 1 5129
10 14 1184
3 15 2326
15 16 374
10 17 5939
11 18 7425
19 9 7678
20 4 1731
21 10 6589
22 15 6826
23 2 8624
17 24 6359
3 25 8616
14 26 2558
18 27 9808
28 22 3697
29 24 7734
2 30 6093
31 5 5374
32 27 5515
24 33 9738
10 34 271
35 25 32
36 13 1976
37 36 6419
38 28 4066
1 39 5951
16 40 7323
41 23 9626
42 28 8613
43 8 5281
2 44 6810
36 45 1819
5 46 9556
47 32 3061
34 48 1940
13 49 4641
11 50 9574
5
42 49 17 43 20 
*/
//27815

完结

凤头猪肚豹尾

posted @ 2024-07-25 13:12  余亦宸  阅读(114)  评论(1)    收藏  举报