[队列]【NOIP2016D2T2】蚯蚓 题解

来源:互联网 发布:2016淘宝刷单 编辑:程序博客网 时间:2024/06/05 18:23

传送门:
洛谷
UOJ

解题报告

取最小值就直接想到堆,但是注意m为7106,也就是最后可能会有107以上的个数,堆的O(nlogn)显然会TLE,所以需要更优的方案。

首先会发现蚯蚓的增长是很麻烦的,但是这是相对的。所以可以直接让被选出的蚯蚓减少长度而不是让其他蚯蚓增加长度。也就是说所有的蚯蚓其实都是”最初的长度”,然后要切的时候加回原长,切完后的两段又捡回”初始长度”。

然后再思考,每次切完蚯蚓后,两段的长度都不会比原串长。所以其实蚯蚓的“原长”(注意是原长,不是现在的长度,看样例就知道)是递减的,然后,就可以直接开3个队列分别处理0:原长,1:一段和2:另一段。每次3个队列头中找最长的,然后分成两端,分别放在1,2队列后面。

然后注意,因为捡回原长后有可能是负的,也有可能负的太大了。所以需要在找出最大值的时候把初始的负值越小越好,109会被洛谷卡掉,UOJ hack掉,所以直接(2311)。查了两天代码的惨痛教训。

复杂度:
时间:O(m)
空间:O(3m)

#include<cstdio>#include<algorithm>#define LL long long#define INF (((1<<30)-1)<<1)+1using namespace std;int n,m,p,q,t,u,now,que[3][7000005],hed[3],til[3];inline char nc(){    static char buf[100000],*pa=buf,*pb=buf;    return pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++;}inline void readi(int &x){    x=0; char ch=nc();    while ('0'>ch||ch>'9') ch=nc();    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=nc();}}inline bool cmp(const int x,const int y){return x>y;}void _init(){    freopen("earthworm.in","r",stdin);    freopen("earthworm.out","w",stdout);    readi(n); readi(m); readi(u); readi(p); readi(q); readi(t);    for (int i=1;i<=n;i++) readi(que[0][i]); sort(que[0]+1,que[0]+n+1,cmp);}inline int geti(){    int tem=-INF,ans;    for (int i=0;i<3;i++) if (hed[i]<=til[i]&&que[i][hed[i]]>tem) {tem=que[i][hed[i]]; ans=i;}    return ans;}void _solve(){    hed[0]=hed[1]=hed[2]=1; til[1]=til[2]=now=0; til[0]=n;    for (int i=1,ti=t;i<=m;i++,now+=u){        int x=geti(),y=que[x][hed[x]++]+now; if (i==ti) {printf("%d ",y); ti+=t;}        int L=(LL)y*p/q,R=y-L; que[1][++til[1]]=L-now-u; que[2][++til[2]]=R-now-u;    }    printf("\n");    for (int i=1,ti=t;i<=n+m;i++){        int x=geti(),y=que[x][hed[x]++]+now; if (i==ti){ti+=t; printf("%d ",y);}    }}int main(){    _init();    _solve();    return 0;}