• 洛谷P3006 [USACO11JAN]瓶颈Bottleneck(堆模拟)


    传送门

    感觉这题的思路还是挺不错的。然而为啥全网就一个题解而且只有代码……然后我只好看着代码理解了好久……

    题意就是有一棵树,每一个节点向他父亲节点连边,且有一个容量表示每一秒可以经过的牛的数量,每一个点有一堆牛,在满足容量限制的情况下可以不断往祖先跳直到跳到1节点。然后问你在保证总时间最短的情况下第$t$秒1节点有多少头牛

    首先,不难发现一个贪心,就是如果向父亲的边能够流满的时候,流满一定比不流满优

    那么我们令每一条边都强制流满,然后对每个点记录一个值$pass[i]$,值为它能向父亲流的最大的流量减去它的儿子们向它流的最大流量,不难发现它代表如果每条边都流满之后每秒能有多少头牛离开这个点向祖先去。

    那么我们设每一个节点开始时牛的个数为$cow[i]$,那么,$cow[i]/pass[i]$就代表这一个节点的所有牛都走光所需要的时间

    那么令$t=cow[i]/pass[i]$,当时间小于等于$t$的时候,我们需要考虑这一个点还剩下多少头牛。当时间大于$t$的时候,我们已经不需要再考虑这个点还剩下多少头牛了,因为可以在满流之后让它所有的牛都到它的父亲那里去。那么,我们可以把它和它的父亲看做同一个点,牛的数量为两个点之和,$pass[fa[i]]$也是两个点之和(它和父亲之间的那条边的流量因为父亲减一次它加一次已经抵消了),然后再对这个点记录一个新的$t$就好了。这个可以用一个并查集维护

    那么,我们对询问按时间排序。当询问的时间大于当前$t$的时候,我们把所有$t$小于等于询问的时间的点全都和它的父亲给并起来。当询问的时间小于等于当前$t$时,答案就是$cow[1]+pass[1]*询问的时间$($cow[1]$代表所有已经被缩到这一个点的总的牛的数量,然后1点的pass肯定是负数,所以减去就相当于加上这个点的儿子的点全都满流向它流,在询问的这段时间里能流多少)

    然后总不可能维护时间轴……所以开个优先队列把所有点的$t$给扔进去就好了,反正就这些点的$t$有用

    讲的应该还蛮清楚的吧……

     1 // luogu-judger-enable-o2
     2 //minamoto
     3 #include<iostream>
     4 #include<cstdio>
     5 #include<cstring>
     6 #include<algorithm>
     7 #include<queue>
     8 #define ll long long
     9 using namespace std;
    10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    11 char buf[1<<21],*p1=buf,*p2=buf;
    12 inline ll read(){
    13     #define num ch-'0'
    14     char ch;bool flag=0;ll res;
    15     while(!isdigit(ch=getc()))
    16     (ch=='-')&&(flag=true);
    17     for(res=num;isdigit(ch=getc());res=res*10+num);
    18     (flag)&&(res=-res);
    19     #undef num
    20     return res;
    21 }
    22 char sr[1<<21],z[20];int C=-1,Z;
    23 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    24 inline void print(ll x){
    25     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    26     while(z[++Z]=x%10+48,x/=10);
    27     while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
    28 }
    29 const int N=100005;
    30 struct query{ll t,res;int id;}ask[N];
    31 inline bool cmp1(query x,query y){return x.t<y.t;}
    32 inline bool cmp2(query x,query y){return x.id<y.id;}
    33 struct node{
    34     ll t;int x;node(){}
    35     node(ll t,int x):t(t),x(x){}
    36     inline bool operator <(const node &b)const
    37     {return t>b.t;}
    38 };
    39 priority_queue<node> q;
    40 int fa[N],f[N],lim[N];ll cow[N],pass[N];
    41 int find(int x){
    42     return fa[x]==x?x:fa[x]=find(fa[x]);
    43 }
    44 int n,m;
    45 int main(){
    46 //    freopen("testdata.in","r",stdin);
    47     n=read(),m=read();
    48     for(int i=1;i<=n;++i) fa[i]=i;
    49     for(int i=2;i<=n;++i)
    50     f[i]=read(),cow[i]=read(),lim[i]=read(),pass[f[i]]-=lim[i],pass[i]+=lim[i];
    51     for(int i=1;i<=m;++i)
    52     ask[i].t=read(),ask[i].id=i;
    53     sort(ask+1,ask+1+m,cmp1);
    54     for(int i=2;i<=n;++i)
    55     if(pass[i]>0)
    56     q.push(node(cow[i]/pass[i],i));
    57     int l=1,x,tp;
    58     while(!q.empty()&&l<=m){
    59         while(l<=m&&ask[l].t<=q.top().t)
    60         ask[l].res=cow[1]-pass[1]*ask[l].t,++l;
    61         if(fa[q.top().x]!=q.top().x){q.pop();continue;}
    62         x=q.top().x,tp=find(f[x]),cow[tp]+=cow[x];
    63         pass[tp]+=pass[x],fa[x]=tp;
    64         if(pass[tp]>0) q.push(node(cow[tp]/pass[tp],tp));
    65         q.pop();
    66     }
    67     sort(ask+1,ask+1+m,cmp2);
    68     for(int i=1;i<=m;++i) print(ask[i].res);
    69     Ot();
    70     return 0;
    71 }
  • 相关阅读:
    c++获取线程id
    一个基于c++的log库
    防止socket程序重启等待2MSL时间
    c++头文件循环引用
    Myeclipse 8.5 优化设置
    来道题 求解释
    MyEclipse常用设置笔记
    Ubuntu 学习笔记
    Linux 下常用命令
    Oracle 学习笔记 常用查询命令篇
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9622691.html
Copyright © 2020-2023  润新知