luogu1084【2012提高】疫情控制(二分答案+贪心+倍增)

来源:互联网 发布:网络百家家乐是真的吗 编辑:程序博客网 时间:2024/06/05 00:55

这题可真是厉害死了。。。调了一天。。。
别看我D2T2考了二分答案,D2T3也是二分答案哟!!惊不惊喜!意不意外!什么?你说我考过贪心和倍增了??那是不是更惊喜啦!更意外啦!
求最小的调动时间,显然调动时间越大,越可能满足条件。所以我们二分这个答案,把原问题变为给定调动时间,判断可行性的问题。显然,一支军队越往跟走能覆盖的边境越多。我们根据调动时间可以把原军队分成两类:(用树上倍增快速实现)
1、到不了根的,那就尽量的往根走,显然这样最优。
2、能到跟的,那就都先到根等着,并算出到跟后的剩余时间。
对于那些没到跟的,他们已经是最优的了,一遍dfs记录哪些点已经被覆盖了(一个点被覆盖,当且仅当他的所有儿子被覆盖)。我们现在只关心根的儿子们,我们要调动那些还有剩余时间的军队来覆盖那些没有被覆盖的儿子们。怎么分配军队呢?我们只好贪心地分配。把那些到根的军队按剩余时间从小到大排序,记作q数组,把那些需要被覆盖的根的儿子按到根的距离从小到大排序,记作b数组。我们贪心地让剩余时间小的去覆盖需要时间小的。但还有个细节,如果一支军队是从a这个儿子上来的,到了根,但剩余时间不足以再回到a了,我们让他直接留在a就好了,这样肯定更优,可以证明的:
如果这只军队去了别的地方x点,然后y军队来填补a,由已知得d(a)>d(x)显然让y去x,这支军队驻扎在A更好。

#include <bits/stdc++.h>using namespace std;#define ll long long#define inf 0x3f3f3f3f#define pa pair<ll,int>#define N 50010inline int read(){    int x=0,f=1;char ch=getchar();    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;}int n,m,h[N],num=0,a[N],fa[N][18],son[N],Log[N],bel[N];ll d[N][18],l=0,r=500000,ans=0;bool mark[N],used[N];struct edge{    int to,next,val;}data[N<<1];void dfs(int x,int tp){    bel[x]=tp;    for(int i=h[x];i;i=data[i].next){        int y=data[i].to;if(y==fa[x][0]) continue;        fa[y][0]=x;d[y][0]=data[i].val;dfs(y,tp);    }}void dfs1(int x){    bool flag=1,fson=0;    for(int i=h[x];i;i=data[i].next){        int y=data[i].to;if(y==fa[x][0]) continue;fson=1;        dfs1(y);if(!mark[y]) flag=0;    }    if(fson&&flag) mark[x]=1;}bool jud(ll tot){    memset(mark,0,sizeof(mark));memset(used,0,sizeof(used));    vector<pa>q;vector<pa>b;    for(int i=1;i<=m;++i){        int x=a[i];ll dx=0;        for(int j=Log[n];j>=0;--j)            if(fa[x][j]&&dx+d[x][j]<=tot)                dx+=d[x][j],x=fa[x][j];        if(x!=1) mark[x]=1;        else q.push_back(make_pair(tot-dx,bel[a[i]]));    }dfs1(1);if(mark[1]) return 1;    for(int i=1;i<=son[0];++i){        int y=son[i];if(mark[y]) continue;        b.push_back(make_pair(d[y][0],y));    }sort(b.begin(),b.end());sort(q.begin(),q.end());    for(int i=0;i<q.size();++i){//到了根以后的剩余时间不够再回来,那干脆就别去根了         int x=q[i].second;        if(!mark[x]&&q[i].first<d[x][0]) mark[x]=1,used[i]=1;    }int pq=0,pb=0;    while(pq<q.size()&&pb<b.size()){//贪心地分配军队         if(used[pq]){pq++;continue;}        if(mark[b[pb].second]){pb++;continue;}        if(q[pq].first<b[pb].first) pq++;        else pq++,pb++;    }while(pb<b.size()&&mark[b[pb].second]) pb++;    if(pb==b.size()) return 1;    else return 0;}int main(){//  freopen("blockade4.in","r",stdin);    n=read();Log[0]=-1;for(int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;    for(int i=1;i<n;++i){        int x=read(),y=read(),val=read();        data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].val=val;        data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].val=val;        if(x!=1&&y!=1) continue;        son[++son[0]]= x==1?y:x;        fa[son[son[0]]][0]=1;d[son[son[0]]][0]=val;    }m=read();if(son[0]>m){puts("-1");return 0;}    for(int i=1;i<=son[0];++i) dfs(son[i],son[i]);    for(int i=1;i<=Log[n];++i)        for(int j=1;j<=n;++j)            fa[j][i]=fa[fa[j][i-1]][i-1],d[j][i]=d[fa[j][i-1]][i-1]+d[j][i-1];    for(int i=1;i<=m;++i) a[i]=read();    while(l<=r){        ll mid=l+r>>1;        if(jud(mid)) r=mid-1,ans=mid;        else l=mid+1;    }    printf("%lld\n",ans);    return 0;}
原创粉丝点击