[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就一定是单调的。
这是我的感性理解,下面是洛谷上翻到的专业证明:
也就是说我根本不需要单调队列,普通的就行。
这样改过之后洛谷上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 }
在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 }
自己选用吧,各有利弊,如下图:
幸甚至哉,歌以咏志。