zoukankan      html  css  js  c++  java
  • [NOIP2016] 蚯蚓

    题目

    原地址(特殊符号太多,还得调格式,节能,没必要的事坚决不做,直接传送吧)

    解说

    第一想法:暴力模拟。

    第二想法:不行,肯定TLE。

    那么有什么方法可以加快速度呢?

    如果没有蚯蚓生长的话一切非常简单。我们准备三个队列q1 q2 q3,操作期间一直保持队首的数最大,所以用3个优先队列。初始时先把原长度全推进q1里,之后每次取三个队列的队首元素,比较找出最大的切割,切割出来较大的一份推进q2里,另一份推进q3里,执行m次这一过程(其实到这里还是像模拟)。但是之后怎么处理生长是个问题。我翻遍了洛谷的3页题解,找了一份最简明易懂的:

     

    这时你会产生一个疑问:怎样让蚯蚓生长呢?

    我们发现,暴力将每次每只蚯蚓的长度加上q是不现实的,所以我定义了一个y,用来表示蚯蚓增长的长度,y初始时为零,每过一秒加上一个q。然后我们可以这样想:

    在每一秒初,最长的蚯蚓被切开;在这一秒过程中,其他蚯蚓增长了q;在这一秒末,最长的蚯蚓被切成两半,分别压入数组中:

    ans[i]=getmax()+y;
    long long j=ans[i]*u/v,k=ans[i]-j;
    y+=q;
    put(j-y,k-y);

    为什么会有一个加y和一个减y的操作呢?

    数学证明:如果第a秒(此时y=(a-1)q)初取出一个蚯蚓s,那么它的实际长度是(s+(a-1)q),它会被切成两半s1和s2在第a+1末被放回去,s1、s2就是这两条新蚯蚓的初始长度,而放回去的是s1-aq和s2-aq。如果在第b+1秒初s1-aq被取了出来,这时它会加上bq成为s1-aq+bq。而在第a秒末到第b+1秒初中间有b-a秒,s1的长度应该是初始长度s1加上增加的长度(b-a)q即s1+(b-a)q=s1-aq+bq。算法成立

    以上有点绕,你也可以感性的理解为:队列中存储了蚯蚓的相对长度,除了被切的蚯蚓都增长就相当于被切的蚯蚓缩短了,最后求答案时再加上增加的长度就可以了

    引自Flash千殇的洛谷博客

     

    就是这样。于是我写出了我的第一代代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define maxn 7000003
     5 ll n,m,u,v,t,a[maxn],ans[maxn],y,q,tot;
     6 priority_queue<ll> q1,q2,q3;
     7 ll getmax(){
     8     ll x1=-(1<<30),x2=x1,x3=x1;
     9     if(!q1.empty())x1=q1.top();
    10     if(!q2.empty())x2=q2.top();
    11     if(!q3.empty())x3=q3.top();
    12     if(x1>=x2&&x1>=x3){q1.pop();return x1;}
    13     else if(x2>=x1&&x2>=x3){q2.pop();return x2;}
    14     q3.pop();return x3;
    15 }
    16 void push(ll a,ll b){
    17     if(a<b) swap(a,b);
    18     q2.push(a);
    19     q3.push(b);
    20 } 
    21 int main(){
    22     scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&q,&u,&v,&t);
    23     for(ll i=1;i<=n;i++) cin>>a[i];
    24     for(ll i=1;i<=n;i++) q1.push(a[i]);
    25     for(ll i=1;i<=m;i++){
    26         ans[i]=getmax()+y;
    27         int j=ans[i]*u/v,k=ans[i]-j;
    28         y+=q;
    29         push(j-y,k-y);
    30     }
    31     while(!q1.empty()||!q2.empty()||!q3.empty()){
    32         a[++tot]=getmax()+y;
    33     }
    34     for(int i=t;i<=m;i+=t) printf("%lld ",ans[i]);
    35     printf("
    ");
    36     for(int i=t;i<=tot;i+=t) printf("%lld ",a[i]);
    37     return 0;
    38 }
    第一代

    结果?TLE 4个点,80分。

    不应该啊,思路一样为什么我TLE呢?

    这时我想起既然我切的时候已经保证了我取出来的是最大的,那么切完之后形成的q1 q2 q3就一定是单调的。

    这是我的感性理解,下面是洛谷上翻到的专业证明:

    (引自aiyougege的洛谷博客)

    也就是说我根本不需要单调队列,普通的就行。

    这样改过之后洛谷上A了。

     1 #include<queue>
     2 #include<cstdio>
     3 #include<iostream>
     4 #include<algorithm>
     5 #define ll long long
     6 using namespace std;
     7 ll n,m,q,u,v,t,a[7500000],ans[7500000],s=0,y=0,tot=0;
     8 queue<ll> q1,q2,q3;
     9 ll getmax(){
    10     int x1=-(1<<30),x2=x1,x3=x1;
    11     if(!q1.empty())x1=q1.front();
    12     if(!q2.empty())x2=q2.front();
    13     if(!q3.empty())x3=q3.front();
    14     if(x1>=x2&&x1>=x3){q1.pop();return x1;}
    15     else if(x2>=x1&&x2>=x3){q2.pop();return x2;}
    16     q3.pop();return x3;
    17 }
    18 void put(ll x1,ll x2){
    19     if(x1<x2){int z=x1;x1=x2;x2=z;}
    20     q2.push(x1),q3.push(x2);
    21     return;
    22 }
    23 bool cmp(ll x,ll y){
    24     return x>y;
    25 }
    26 inline ll read(){
    27     char ch=getchar();ll j=0,k=1;
    28     while(ch<'0'||ch>'9'){if(ch=='-')k=-1;ch=getchar();}
    29     while(ch>='0'&&ch<='9')j=j*10+ch-'0',ch=getchar();
    30     return j*k;
    31 }
    32 inline void write(ll x){
    33     if(x<0)putchar('-'),x=-x;
    34     if(x>9)write(x/10);
    35     putchar(x%10+'0');
    36     return;
    37 }
    38 int main(){
    39     n=read(),m=read(),q=read(),u=read(),v=read(),t=read();
    40     for(ll i=1;i<=n;i++)a[i]=read();
    41     sort(a+1,a+n+1,cmp);
    42     for(ll i=1;i<=n;i++)q1.push(a[i]);
    43     for(ll i=1;i<=m;i++){
    44         ans[i]=getmax()+y;
    45         int j=ans[i]*u/v,k=ans[i]-j;
    46         y+=q;
    47         put(j-y,k-y);
    48     }
    49     while(!q1.empty()||!q2.empty()||!q3.empty())a[++tot]=getmax()+y;//最后剩下的蚯蚓按降序存储
    50     for(int i=t;i<=m;i+=t)write(ans[i]),putchar(' ');
    51     putchar('
    ');
    52     for(int i=t;i<=tot;i+=t)write(a[i]),putchar(' ');
    53     putchar('
    ');
    54     return 0;
    55 }
    洛谷第一代AC代码

    在Vjudge上交一下吧,然后……RE!

    ???要是TLE我还能理解为什么会RE啊这是抓取的什么神奇网站啊???

    总之检查吧。

    之后问了老师的意见,他觉得可能getmax()有点问题可以换手模队列试试,然后两个版本累计交了将近十遍一直RE……

    之后就请教了吉吉大佬,他觉得我队列没判empty。

    我寻思你开玩笑呢?下面这一大堆不是判空是啥?

    if(!q1.empty())x1=q1.front();
    if(!q2.empty())x2=q2.front();
    if(!q3.empty())x3=q3.front();

    但是我还是改了改试试,变成了下面这样:

    int getmax(){
        int x1=-(1<<30),x2=x1,x3=x1;
        if(!q1.empty())x1=q1.front();
        if(!q2.empty())x2=q2.front();
        if(!q3.empty())x3=q3.front();
        if(x1>=x2&&x1>=x3){
            if(!q1.empty()){
                q1.pop();
                return x1;
            }
        }
        else if(x2>=x1&&x2>=x3){
            if(!q2.empty()){
                q2.pop();
                return x2;
            }
        }
        if(!q3.empty()){
            q3.pop();
            return x3;
        }
    }

    然后就A了……

    不说了直接%%%。

    但是为什么之前的不对呢?显然即便我前面判了空,后面依然会继续运行,而且后面是大于等于,若q1 q2均空就直接炸了。

    细节!

    顺带一提,看范围这道题不用开long long,int能存下,就计算j=ans[i]*u/v的时候数据类型转换一下即可,还能省点时间。

    代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define maxn 8000000
     4 int n,m,u,v,t,a[maxn],ans[maxn],y,q,tot;
     5 queue<int> q1,q2,q3;
     6 bool cmp(int a,int b){
     7     return a>b;
     8 }
     9 int getmax(){
    10     int x1=-(1<<30),x2=x1,x3=x1;
    11     if(!q1.empty())x1=q1.front();
    12     if(!q2.empty())x2=q2.front();
    13     if(!q3.empty())x3=q3.front();
    14     if(x1>=x2&&x1>=x3){
    15         if(!q1.empty()){
    16             q1.pop();
    17             return x1;
    18         }
    19     }
    20     else if(x2>=x1&&x2>=x3){
    21         if(!q2.empty()){
    22             q2.pop();
    23             return x2;
    24         }
    25     }
    26     if(!q3.empty()){
    27         q3.pop();
    28         return x3;
    29     }
    30 }
    31 void push(int a,int b){
    32     if(a<b) swap(a,b);
    33     q2.push(a);
    34     q3.push(b);
    35 } 
    36 int main(){
    37     cin>>n>>m>>q>>u>>v>>t;
    38     for(int i=1;i<=n;i++) cin>>a[i];
    39     sort(a+1,a+1+n,cmp);
    40     for(int i=1;i<=n;i++) q1.push(a[i]);
    41     for(int i=1;i<=m;i++){
    42         ans[i]=getmax()+y;
    43         int j=(long long)ans[i]*u/v,k=ans[i]-j;
    44         y+=q;
    45         push(j-y,k-y);
    46     }
    47     while(!q1.empty()||!q2.empty()||!q3.empty()){
    48         a[++tot]=getmax()+y;
    49     }
    50     for(int i=t;i<=m;i+=t) cout<<ans[i]<<' ';
    51     cout<<endl;
    52     for(int i=t;i<=tot;i+=t) cout<<a[i]<<' ';
    53     return 0;
    54 }
    完美版队列代码
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define maxn 15000000
     4 int n,m,u,v,t,a[maxn],ans[maxn],y,q,tot;
     5 int q1[maxn],q2[maxn],q3[maxn],f1,f2,f3,t1,t2,t3;
     6 int getmax(){
     7     int x1=-(1<<30),x2=x1,x3=x1;
     8     if(t1!=f1)x1=q1[f1+1];
     9     if(t2!=f2)x2=q2[f2+1];
    10     if(t3!=f3)x3=q3[f3+1];
    11     if(x1>=x2&&x1>=x3){
    12         if(f1!=t1){
    13             q1[f1+1]=0;
    14             f1++;
    15             return x1;
    16         }
    17     }
    18     else if(x2>=x1&&x2>=x3){
    19         if(f2!=t2){
    20             q2[f2+1]=0;
    21             f2++;
    22             return x2;
    23         }
    24     }
    25     if(f3!=t3){
    26         q3[f3+1]=0;
    27         f3++;
    28         return x3;
    29     }
    30 }
    31 void push(int a,int b){
    32     if(a<b) swap(a,b);
    33     t2++; q2[t2]=a;
    34     t3++; q3[t3]=b;
    35 } 
    36 int main(){
    37     cin>>n>>m>>q>>u>>v>>t;
    38     for(int i=1;i<=n;i++) cin>>a[i];
    39     sort(a+1,a+1+n);
    40     for(int i=n;i>=1;i--){
    41         t1++;
    42         q1[t1]=a[i];
    43     }
    44     for(int i=1;i<=m;i++){
    45         int z=getmax();
    46         ans[i]=z+y;
    47         int j=(long long)ans[i]*u/v,k=ans[i]-j;
    48         y+=q;
    49         push(j-y,k-y);
    50     }
    51     while(t1!=f1||t2!=f2||t3!=f3){
    52         a[++tot]=getmax()+y;
    53     }
    54     for(int i=t;i<=m;i+=t) cout<<ans[i]<<' ';
    55     cout<<endl;
    56     for(int i=t;i<=tot;i+=t) cout<<a[i]<<' ';
    57     return 0;
    58 }
    完美版手模代码

    自己选用吧,各有利弊,如下图:

    幸甚至哉,歌以咏志。

  • 相关阅读:
    《构建之法》
    《构建之法》第一单元
    查询特殊字符
    Excel文件批量导入SQLSERVER数据库中(利用Foreach容器)
    当月的最后一天SELECT DATEADD(dd,1,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0)) 20140930 00:00:00.000
    the difference between primary key and unique key
    sql中如何再判断一个字段是否为空,如果不为空然后再Select这个字段,这要如何写呢?
    union和union all的区别
    UIImageView的基本使用
    UINavigationController导航控制器
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12714798.html
Copyright © 2011-2022 走看看