[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("\n");
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('\n');
52     for(int i=t;i<=tot;i+=t)write(a[i]),putchar(' ');
53     putchar('\n');
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 }
完美版手模代码

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

 

幸甚至哉,歌以咏志。

 

posted @ 2020-04-16 19:08  DarthVictor  阅读(263)  评论(0编辑  收藏  举报
莫挨老子!