P1084 [NOIP2012 提高组] 疫情控制 (二分答案、贪心)

因为若一个时间限制满足题意,则所有比它大的时间限制一定都满足题意,因此本题答案具有单调性,可以想到二分答案求解。

本题思路不是很难,但细节和代码实现比较复杂。

见牛人博客:https://www.luogu.com.cn/blog/TEoS/p1084-yi-qing-kong-zhi

复制代码
  1 #include<bits/stdc++.h>
  2 typedef long long ll;
  3 using namespace std;
  4 const int N=6e4;
  5 int n,m,t,tot,atot,btot,ctot;
  6 int d[N],query[N],f[N][20];
  7 int to[N<<1],edge[N<<1],nxt[N<<1],head[N];
  8 bool ok,sta[N],need[N];
  9 ll ans,tim[N],ned[N],dist[N][20];
 10 pair<ll,int> h[N];
 11 queue<int> q;
 12 void add(int x,int y,int z){
 13     nxt[++tot]=head[x];head[x]=tot;to[tot]=y;edge[tot]=z;
 14 }
 15 
 16 void bfs(){
 17     q.push(1);d[1]=1;
 18     while(q.size()){
 19         int x=q.front();q.pop();
 20         for(int i=head[x];i;i=nxt[i]){
 21             int y=to[i];
 22             if(d[y]) continue;
 23             d[y]=d[x]+1;
 24             f[y][0]=x;dist[y][0]=edge[i];
 25             for(int j=1;j<=t;j++){
 26                 f[y][j]=f[f[y][j-1]][j-1];
 27                 dist[y][j]=dist[y][j-1]+dist[f[y][j-1]][j-1];
 28             }
 29             q.push(y);
 30         }
 31     }
 32 }
 33 
 34 void init(){
 35     memset(sta,0,sizeof(sta));
 36     memset(tim,0,sizeof(tim));
 37     memset(ned,0,sizeof(ned));
 38     memset(h,0,sizeof(h));
 39     memset(need,0,sizeof(need));
 40     atot=0,btot=0,ctot=0;
 41 }
 42 
 43 bool dfs(int x){
 44     bool pson=0;
 45     if(sta[x]) return 1;
 46     for(int i=head[x];i;i=nxt[i]){
 47         int y=to[i];
 48         if(d[y]<d[x]) continue;
 49         pson=1;
 50         if(!dfs(y)) return 0;
 51     }
 52     if(!pson) return 0;
 53     return 1;
 54 }//如果是叶子结点,pson=0,那么该路径没有军队驻扎 
 55 
 56 bool ck(ll lim){
 57     for(int i=1;i<=m;i++){//上移军队并处理闲置军队
 58         ll x=query[i],cnt=0;
 59         for(int j=t;j>=0;j--)
 60             if(f[x][j]>1/*不能是根节点*/ && cnt+dist[x][j]<=lim){
 61                 cnt+=dist[x][j];
 62                 x=f[x][j];
 63             }
 64         if(f[x][0]==1 && cnt+dist[x][0]<=lim)
 65             h[++ctot]=make_pair(lim-cnt-dist[x][0],x);
 66         else sta[x]=1;
 67     }
 68     for(int i=head[1];i;i=nxt[i])
 69         if(!dfs(to[i])) need[to[i]]=1;//dfs寻找路径未被驻扎的叶子节点
 70     sort(h+1,h+ctot+1);//第一关键字排序
 71     for(int i=1;i<=ctot;i++)
 72         if(need[h[i].second] && h[i].first<dist[h[i].second][0]) 
 73             need[h[i].second]=0;
 74         else tim[++atot]=h[i].first;//对根节点的需要被驻扎的子节点进行初步处理
 75     for(int i=head[1];i;i=nxt[i])
 76         if(need[to[i]]) ned[++btot]=dist[to[i]][0];//找到仍需要被驻扎的节点并存储
 77     if(atot<btot) return 0;//无解
 78     sort(ned+1,ned+btot+1);
 79     int i=1,j=1;
 80     while(i<=btot && j<=atot)//双指针扫描 
 81         if(tim[j]>=ned[i]) i++,j++;
 82         else j++;
 83     if(i>btot) return 1;
 84     return 0;
 85 }
 86 
 87 int main(){
 88     cin>>n;ll l=0,r=0,mid;
 89     t=log2(n)+1;
 90     for(int i=1;i<n;i++){
 91         int x,y,z;scanf("%d%d%d",&x,&y,&z);
 92         add(x,y,z);add(y,x,z);r+=z;
 93     }
 94     bfs();//树上倍增预处理 
 95     cin>>m;for(int i=1;i<=m;i++) scanf("%d",&query[i]);//存每个军队
 96     while(l<=r){//二分答案 
 97         init();mid=(l+r)>>1;
 98         if(ck(mid)) r=mid-1,ans=mid,ok=1;
 99         else l=mid+1;
100     } 
101     if(!ok) cout<<-1<<endl;
102     else cout<<ans<<endl;
103     return 0;
104 }
复制代码

 



如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   YHXo  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示