洛谷P1084:疫情控制 (二分答案+倍增+贪心)

来源:互联网 发布:hecras软件 编辑:程序博客网 时间:2024/05/22 12:02

题目传送门:https://www.luogu.org/problem/show?pid=1084


题目分析:首先发现答案具有单调性,于是二分答案。二分了之后,发现每支军队肯定是往上走最优,于是预处理倍增,快速向上跳。接下来我们要处理的就是,如果一支军队可以走到根节点,该怎么做?

如果一棵子树中的所有叶子节点,到根的路径中都至少有一个军队,我们就称这棵子树是符合条件的。很明显,我们要将可以调到根节点的军队,转移到不符合条件的根节点的儿子。如果我们记到达根节点的军队i,还能再走的距离为rest[i];那么如果根的儿子u不合法,它在所有能到根的军队中找一个rest值刚好大于边(root,u)长度的军队,将这支军队转移到u,答案会最优。于是将所有rest值和边(root,u)从小到大排个序,扫一遍就行了。

但其实还有一个小细节:如果一支军队i是从节点u跳到根的,而u的子树本身就是不符合条件的,那么军队i驻扎到u,不需要考虑rest[i]和边(root,u)的长度关系。对于一个不符合条件的根节点的儿子u,考虑用它子树中跳上来的军队来驻扎:如果它子树中跳上来的军队rest的最小值,小于边(root,u)的长度,那么用这个军队驻扎在u会更优,因为它可以使一些rest值更大的军队去驻扎别的节点。

这样做时间复杂度就是O((n+m)log(n)log(R)),其中R是二分答案的上界,按道理来说应该要开到51013,但在洛谷测会超时,其实只要开到5105就可以了。还有,本题数据没有-1,考场上写了输出-1就送命……
(写代码的时候有一个下标from[i]写成了i,码了个对拍调半天才调出来)


CODE:

#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=50010;const int maxl=18;const long long oo=5e5;typedef long long LL;struct edge{    int obj,len;    edge *Next;} e[maxn<<1];edge *head[maxn];int cur=-1;LL dis[maxn][maxl];int fa[maxn][maxl];bool Son[maxn];int dep[maxn];LL cnt[maxn];int from[maxn];int num;LL Min[maxn];int id[maxn];bool vis[maxn];bool full[maxn];int army[maxn];int n,m;void Add(int x,int y,int z){    cur++;    e[cur].obj=y;    e[cur].len=z;    e[cur].Next=head[x];    head[x]=e+cur;}void Dfs1(int node){    for (edge *p=head[node]; p; p=p->Next)    {        int son=p->obj;        if (son==fa[node][0]) continue;        fa[son][0]=node;        dis[son][0]=p->len;        Dfs1(son);        if (node==1) Son[son]=true,dep[son]=p->len;    }}void Jump(int u,LL v){    for (int j=maxl-1; j>=0; j--)        if ( dis[u][j]<=v && fa[u][j]!=1 )            v-=dis[u][j],u=fa[u][j];    if ( Son[u] && ((long long)dep[u])<v ) cnt[++num]=v-(long long)dep[u],from[num]=u;    else vis[u]=true;}bool Dfs2(int node){    if (vis[node]) return true;    bool f=true,leaf=true;    for (edge *p=head[node]; p; p=p->Next)    {        int son=p->obj;        if (son==fa[node][0]) continue;        leaf=false;        f&=Dfs2(son);    }    if (leaf) return false;    return f;}bool Judge(LL v){    num=0;    for (int i=1; i<=n; i++) Min[i]=oo+1LL,vis[i]=full[i]=false;    for (int i=1; i<=m; i++) Jump(army[i],v);    for (int i=1; i<=n; i++) if (Son[i]) full[i]=Dfs2(i);    for (int i=1; i<=num; i++)        if ( !full[ from[i] ] && cnt[i]<Min[ from[i] ] && cnt[i]<=((long long)dep[ from[i] ]) )            Min[ from[i] ]=cnt[i],id[ from[i] ]=i;    for (int i=1; i<=n; i++)        if (Min[i]<=oo) cnt[ id[i] ]=0;    int x=0;    for (int i=1; i<=n; i++)        if ( Son[i] && !full[i] && Min[i]>oo ) id[++x]=dep[i];    if (!x) return true;    if (!num) return false;    sort(cnt+1,cnt+num+1);    sort(id+1,id+x+1);    int tail=1,sol=1;    for (int i=1; i<=x; i++)    {        while ( tail<=num && cnt[tail]<((long long)id[i]) ) tail++;        if (tail==num+1)        {            sol=0;            break;        }        tail++;    }    return sol;}LL Binary(){    LL L=-1LL,R=oo+1LL;    while (L+1LL<R)    {        LL mid=(L+R)>>1LL;        if ( Judge(mid) ) R=mid;        else L=mid;    }    return R;}int main(){    freopen("1084.in","r",stdin);    freopen("1084.out","w",stdout);    scanf("%d",&n);    for (int i=1; i<=n; i++) head[i]=NULL;    for (int i=1; i<n; i++)    {        int x,y,z;        scanf("%d%d%d",&x,&y,&z);        Add(x,y,z);        Add(y,x,z);    }    scanf("%d",&m);    for (int i=1; i<=m; i++) scanf("%d",&army[i]);    fa[1][0]=1;    Dfs1(1);    for (int j=1; j<maxl; j++)        for (int i=1; i<=n; i++)            fa[i][j]=fa[ fa[i][j-1] ][j-1],            dis[i][j]=dis[i][j-1]+dis[ fa[i][j-1] ][j-1];    LL ans=Binary();    if (ans<=oo) printf("%lld\n",ans);    else printf("-1\n");    return 0;}