此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。
题目链接:https://www.luogu.org/problemnew/show/2827
题目描述
本题中,我们将用符号【c】表示对c向下取整,例如:【3.0】=【3.1】=【3.9】=3.
蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。
蛐蛐国里现在共有n只蚯蚓(n为正整数)。每只蚯蚓拥有长度,我们设第i只蚯蚓的长度为ai(i=1,2,...,n),并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。
神刀手切开蚯蚓的位置由常数p(是满足0<p<1的有理数)决定,设这只蚯蚓长度为x,神刀手会将其切成两只长度分别为【px】,【x-px】的蚯蚓。
特殊地,如果这两个数的其中一个等于0,则这个长度为0的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加q(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要m秒才能到来......
(m为非负整数)
蛐蛐国王希望知道这m秒内的战况。具体来说,他希望知道:
•m秒内,每一秒被切断的蚯蚓被切断前的长度(有m个数)
•m秒后,所有蚯蚓的长度(有n+m个数)。
蛐蛐国王当然知道怎么做啦!但是他想考考你......
输入输出格式
输入格式:第一行包含六个整数n,m,q,u,v,t,其中:n,m,q的意义见【问题描述】;
u,v,t均为正整数;你需要自己计算p=u/v(保证0<u<v)t是输出参数,其含义将会在【输出格式】中解释。
第二行包含n个非负整数,为ai,a2,...,an,即初始时n只蚯蚓的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。
数据范围见下表。
输出格式:第一行输出【m/t】个整数,按时间顺序,依次输出第t秒,第2t秒,第3t秒……被切断蚯蚓(在被切断前)的长度。
第二行输出【(n+m)/t】个整数,输出m秒后蚯蚓的长度;需要按从大到小的顺序,依次输出排名第t,第2t,第3t……的长度。
同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要 输出,你也应输出一个空行。
请阅读样例来更好地理解这个格式。
输入输出样例
3 7 1 1 3 1 3 3 2
3 4 4 4 5 5 6 6 6 6 5 5 4 4 3 2 2
3 7 1 1 3 2 3 3 2
4 4 5 6 5 4 3 2
3 7 1 1 3 9 3 3 2
//空行 2
说明
【样例解释1】
在神刀手到来前:3只蚯蚓的长度为3,3,2。
1秒后:一只长度为3的蚯蚓被切成了两只长度分别为1和2的蚯蚓,其余蚯蚓的长度增加了1。
最终4只蚯蚓的长度分别为(1,2),4,3。括号表示这个位置刚刚有一只蚯蚓被切断
2秒后:一只长度为4的蚯蚓被切成了1和3。5只蚯蚓的长度分别为:2,3,(1,3),4。
3秒后:一只长度为4的蚯蚓被切断。6只蚯蚓的长度分别为:3,4,2,4,(1,3)。
4秒后:一只长度为4的蚯蚓被切断。7只蚯蚓的长度分别为:4,(1,3),3,5,2,4。
5秒后:一只长度为5的蚯蚓被切断。8只蚯蚓的长度分别为:5,2,4,4,(1,4),3,5。
6秒后:一只长度为5的蚯蚓被切断。9只蚯蚓的长度分别为:(1,4),3,5,5,2,5,4,6。
7秒后:一只长度为6的蚯蚓被切断。10只蚯蚓的长度分别为:2,5,4,6,6,3,6,5,(2,4)。
所以,7秒内被切断的蚯蚓的长度依次为3,4,4,4,5,5,6。7秒后,所有蚯蚓长度从大到小排序为6,6,6,5,5,4,4,3,2,2
【样例解释2】
这个数据中只有t=2与上个数据不同。只需在每行都改为每两个数输出一个数即可。
虽然第一行最后有一个6没有被输出,但是第二行仍然要重新从第二个数再开始输出。
【样例解释3】
这个数据中只有t=9与上个数据不同。
注意第一行没有数要输出,但也要输出一个空行。
【数据范围】
我恨markdown。
分析:
最暴力的想法大概就是每一秒都把所有的蚯蚓取出来,最长的砍断,其余的增加p。
用两个堆维护蚯蚓的长度,整个过程就是两个队列来回换。
复杂度O(nm),能拿35分。不出所料,大片TLE。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 6 inline void read(int &x) 7 { 8 char ch = getchar(),c = ch;x = 0; 9 while(ch < '0' || ch > '9') c = ch,ch = getchar(); 10 while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar(); 11 if(c == '-') x = -x; 12 } 13 14 std::priority_queue <long long> q1,q2; 15 int n,m,q,u,v,t,cnt; 16 long long tmp,a,b; 17 18 int main() 19 { 20 read(n),read(m),read(q),read(u),read(v),read(t); 21 for(int i = 1;i <= n;++ i) 22 { 23 scanf("%lld",&tmp); 24 q1.push(tmp); 25 } 26 for(int i = 1;i <= m;++ i) 27 { 28 tmp = q1.top(); 29 q1.pop(); 30 if(i%t == 0) printf("%lld ",tmp); 31 a = tmp*u/v,b = tmp-a; 32 while(!q1.empty()) 33 { 34 tmp = q1.top(); 35 q1.pop(); 36 q2.push(tmp+q); 37 } 38 q2.push(a),q2.push(b); 39 40 if(i == m) break; 41 else ++ i; 42 43 tmp = q2.top(); 44 q2.pop(); 45 if(i%t == 0) printf("%lld ",tmp); 46 a = tmp*u/v,b = tmp-a; 47 while(!q2.empty()) 48 { 49 tmp = q2.top(); 50 q2.pop(); 51 q1.push(tmp+q); 52 } 53 q1.push(a),q1.push(b); 54 } 55 printf("\n"); 56 while(!q1.empty()){ 57 ++ cnt; 58 tmp = q1.top(); 59 q1.pop(); 60 if(cnt%t == 0) 61 printf("%lld ",tmp); 62 } 63 cnt = 0; 64 while(!q2.empty()){ 65 ++ cnt; 66 tmp = q2.top(); 67 q2.pop(); 68 if(cnt%t == 0) 69 printf("%lld ",tmp); 70 } 71 printf("\n"); 72 return 0; 73 }
但是为什么我会选择把所有的蚯蚓都取出来,而不是直接放回去呢?
十分显然,蚯蚓的长度原本是具有单调性的,因为所有蚯蚓在相同时间内增加的长度相同,所以它们的相对长度不变。
每次切断最长蚯蚓的时候,新产生的两只小蚯蚓是不会变长的,这就破坏了原本的单调性,不能直接加回去。
那么我们把新产生的两只蚯蚓的长度-p,就相当于它们也跟别的蚯蚓一样在这一秒变长了p。
每次进行操作时,把取出的蚯蚓长度加上p*(m-1),放回时再减去p*m,最后输出蚯蚓长度时统一加上k*m即可。
还是要用堆维护,复杂度O(m*logn),80分。有4个点TLE。
这个方法在NOIP中只能拿到65分,洛谷因为开启了O2优化(主要是评测机速度快..)所以分数高一些。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 6 inline void read(int &x) 7 { 8 char ch = getchar(),c = ch;x = 0; 9 while(ch < '0' || ch > '9') c = ch,ch = getchar(); 10 while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar(); 11 if(c == '-') x = -x; 12 } 13 14 inline void read(long long &x) 15 { 16 char ch = getchar(),c = ch;x = 0; 17 while(ch < '0' || ch > '9') c = ch,ch = getchar(); 18 while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar(); 19 if(c == '-') x = -x; 20 } 21 22 std::priority_queue <long long> q; 23 int n,m,k,u,v,t,cnt; 24 long long tmp,a,b,add; 25 26 int main() 27 { 28 read(n),read(m),read(k),read(u),read(v),read(t); 29 for(int i = 1;i <= n;++ i){ 30 read(tmp); 31 q.push(tmp); 32 } 33 for(int i = 1;i <= m;++ i) 34 { 35 tmp = q.top()+k*(i-1); 36 q.pop(); 37 a = tmp*u/v,b = tmp-a; 38 if(i%t == 0) 39 printf("%lld ",tmp); 40 q.push(a-k*i),q.push(b-k*i); 41 } 42 printf("\n"); 43 add = k*m; 44 while(!q.empty()) 45 { 46 ++ cnt; 47 tmp = q.top()+add; 48 if(cnt%t == 0) 49 printf("%lld ",tmp); 50 q.pop(); 51 } 52 printf("\n"); 53 return 0; 54 }
80分就够了不想写了让我堕落
正解是用三个队列,分别维护原有蚯蚓的长度、原蚯蚓被切开后两段蚯蚓的长度。
这三个队列在各自的内部依然是具有单调性的,所以还是用一个额外的变量表示增加的长度。
每次更新的时候取出三个队首中最大的一个切断,然后放到对应的队列里去。
最后输出答案的时候,对三个队列的队首归并,每次取出最大的一个,加上额外的变量。
正确性并不显然,但是不想证了...感觉这个Solution易懂。
AC代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 6 inline void read(int &x) 7 { 8 char ch = getchar(),c = ch;x = 0; 9 while(ch < '0' || ch > '9') c = ch,ch = getchar(); 10 while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar(); 11 if(c == '-') x = -x; 12 } 13 14 inline void read(long long &x) 15 { 16 char ch = getchar(),c = ch;x = 0; 17 while(ch < '0' || ch > '9') c = ch,ch = getchar(); 18 while(ch <= '9' && ch >= '0') x = (x<<1)+(x<<3)+ch-'0',ch = getchar(); 19 if(c == '-') x = -x; 20 } 21 22 std::queue <long long> q[4]; 23 int n,m,k,t,cnt,cnt1,cnt2,cnt3; 24 int num[100002]; 25 long long tmp,u,v,a,b,add; 26 27 int cmp(int a,int b) 28 {return a>b;} 29 30 void calc() 31 { 32 tmp = -2147483647; 33 int id; 34 if(!q[1].empty() && q[1].front() > tmp) 35 id = 1,tmp = q[1].front(); 36 if(!q[2].empty() && q[2].front() > tmp) 37 id = 2,tmp = q[2].front(); 38 if(!q[3].empty() && q[3].front() > tmp) 39 id = 3,tmp = q[3].front(); 40 q[id].pop(); 41 } 42 43 void out() 44 { 45 for(int i = 1;i <= n+m;++ i){ 46 calc(); 47 if(i%t == 0){ 48 tmp += add; 49 printf("%lld ",tmp); 50 } 51 } 52 } 53 54 int main() 55 { 56 read(n),read(m),read(k),read(u),read(v),read(t); 57 for(int i = 1;i <= n;++ i) 58 read(num[i]); 59 std::sort(num+1,num+1+n,cmp); 60 for(int i = 1;i <= n;++ i) 61 q[1].push(1LL*num[i]); 62 for(int i = 1;i <= m;++ i) 63 { 64 calc(); 65 tmp += (i-1)*k; 66 if(i%t == 0) printf("%lld ",tmp); 67 a = tmp*u/v,b = tmp-a; 68 q[2].push(a-i*k),q[3].push(b-i*k); 69 } 70 printf("\n"); 71 add = k*m; 72 out(); 73 printf("\n"); 74 return 0; 75 }