zoukankan      html  css  js  c++  java
  • LUOGU P1084 疫情控制(二分+贪心+树上倍增)

    传送门

    解题思路

      比较神的一道题。首先发现是最小值问题,并且具有单调性,所以要考虑二分答案。其次有一个性质是军队越靠上越优,所以我们要将所有的军队尽量向上提,这一过程我们用倍增实现。发现这时有两种军队,一种是到根之后可以继续移动,另一种不行。那么记录下来那些到根之后可以移动的点和可以继续移动的距离,存到一个结构体里,顺便还要记录下来每个根节点的子节点中剩余路程最短的点。继续一遍(dfs),可以求出那些需要被占领的根节点的子节点,然后记录他们到根的距离。把两个数组从大到小排序,贪心的选取剩余路程大于需要占领的子节点到根的距离的那些点去占领,注意也要考虑到不移动的情况。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    
    using namespace std;
    const int MAXN = 50005;
    typedef long long LL;
    
    inline int rd(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
    	while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    	return f?x:-x;
    }
    
    int n,m,head[MAXN],cnt,to[MAXN<<1],nxt[MAXN<<1],val[MAXN<<1],a[MAXN];
    int f[MAXN][23],ar[MAXN],resid[MAXN],cntb,cnta;
    LL dis[MAXN][23],ans,sum,resmin[MAXN];
    bool vis[MAXN],use[MAXN];
    
    struct Data{
    	int id;LL res;
    	friend bool operator<(const Data A,const Data B){
    		return A.res>B.res;
    	}
    }tmp[MAXN],b[MAXN];
    
    inline void add(int bg,int ed,int w){
    	to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=w,head[bg]=cnt;
    }
    
    void dfs(int x,int fa,int w){
    	f[x][0]=fa;dis[x][0]=w;int u;
    	for(int i=1;i<=20;i++){
    		f[x][i]=f[f[x][i-1]][i-1];
    		dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1];
    //		printf("f[%d][%d]=%d
    ",x,i,f[x][i]);
    	}
    	for(int i=head[x];i;i=nxt[i]){
    		u=to[i];if(u==fa) continue;
    		dfs(u,x,val[i]);
    	}
    }
    
    bool dfs2(int x,int fa){
    	int u;bool flag=false,ok=true;if(vis[x]) return 1;
    	for(int i=head[x];i;i=nxt[i]){
    		u=to[i];if(u==fa) continue;flag=1;
    		if(!dfs2(u,x)){ok=false;
    			if(x==1) b[++cntb].id=u,b[cntb].res=val[i];	
    			else return false;
    		}
    	}
    	if(!flag) return false;
    	return ok;
    }
    
    inline bool check(LL lim){
    	cnta=0,cntb=0;int L=1;
    	memset(use,0,sizeof(use));
    	memset(vis,0,sizeof(vis));
    	memset(resmin,0,sizeof(resmin));
    	memset(resid,0,sizeof(resid));
    	for(int i=1;i<=m;i++){
    		ar[i]=a[i];LL now=lim;
    		for(int j=20;j>=0;j--)
    			if(f[ar[i]][j]>1 && now>=dis[ar[i]][j]) now-=dis[ar[i]][j],ar[i]=f[ar[i]][j];
    		if(f[ar[i]][0]==1 && now-dis[ar[i]][0]>0) {
    			tmp[++cnta].res=now-dis[ar[i]][0];tmp[cnta].id=i;
    			if(tmp[cnta].res<resmin[ar[i]] || !resmin[ar[i]])
    				resmin[ar[i]]=tmp[cnta].res,resid[ar[i]]=i;
    		}
    		else vis[ar[i]]=1;
    	}
    	dfs2(1,0);if(!cntb) return true;
    	sort(tmp+1,tmp+1+cnta);sort(b+1,b+1+cntb);use[0]=1;
    	for(int i=1;i<=cntb;i++){
    		if(!use[resid[b[i].id]]) {use[resid[b[i].id]]=1;continue;}
    		while(L<=cnta && (tmp[L].res<b[i].res || use[tmp[L].id])) L++;
    		if(L>cnta) return false;use[tmp[L].id]=1;L++;
    	}
    	return true;
    }
    
    int main(){
    //	freopen("1.in","r",stdin);
    	n=rd();int x,y,z;
    	for(int i=1;i<n;i++){
    		x=rd(),y=rd(),z=rd();
    		add(x,y,z),add(y,x,z);sum+=z;
    	}
    	m=rd();for(int i=1;i<=m;i++) a[i]=rd();
    	dfs(1,0,0);LL l=0,r=sum,mid;
    	while(l<=r) {
    		mid=(l+r)>>1;
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    /*
    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 
    */
    
  • 相关阅读:
    Docker+Jenkins更换国内插件源
    招聘java工程师
    Apache seaTunnel 数据集成平台
    DBeaver连接MySQL问题的解决
    iphone的mov文件复制到电脑的方法
    IDEA中RestfulToolkit插件的安装及使用
    lwm2m 的notify 的机制
    ALI云接入学习
    CAT1 UIS8910模组对接阿里云平台
    任务的堆栈究竟有何魔力
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9905205.html
Copyright © 2011-2022 走看看