洛谷 P2827 蚯蚓 题解

时间:2023-01-01 19:36:30

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。

题目链接: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……的长度。

同一行中相邻的两个数之间,恰好用一个空格隔开。即使某一行没有任何数需要 输出,你也应输出一个空行。

请阅读样例来更好地理解这个格式。

输入输出样例

输入样例#1: 
3 7 1 1 3 1
3 3 2
输出样例#1: 
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2
输入样例#2: 
3 7 1 1 3 2
3 3 2
输出样例#2: 
4 4 5
6 5 4 3 2
输入样例#3: 
3 7 1 1 3 9
3 3 2
输出样例#3: 
//空行
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与上个数据不同。

注意第一行没有数要输出,但也要输出一个空行。

【数据范围】

洛谷 P2827 蚯蚓 题解

 

我恨markdown。

分析:

最暴力的想法大概就是每一秒都把所有的蚯蚓取出来,最长的砍断,其余的增加p。

用两个堆维护蚯蚓的长度,整个过程就是两个队列来回换。

复杂度O(nm),能拿35分。不出所料,大片TLE。

洛谷 P2827 蚯蚓 题解洛谷 P2827 蚯蚓 题解
 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 }
35'代码

但是为什么我会选择把所有的蚯蚓都取出来,而不是直接放回去呢?

十分显然,蚯蚓的长度原本是具有单调性的,因为所有蚯蚓在相同时间内增加的长度相同,所以它们的相对长度不变。

每次切断最长蚯蚓的时候,新产生的两只小蚯蚓是不会变长的,这就破坏了原本的单调性,不能直接加回去。

那么我们把新产生的两只蚯蚓的长度-p,就相当于它们也跟别的蚯蚓一样在这一秒变长了p。

每次进行操作时,把取出的蚯蚓长度加上p*(m-1),放回时再减去p*m,最后输出蚯蚓长度时统一加上k*m即可。

还是要用堆维护,复杂度O(m*logn),80分。有4个点TLE。

这个方法在NOIP中只能拿到65分,洛谷因为开启了O2优化(主要是评测机速度快..)所以分数高一些。

洛谷 P2827 蚯蚓 题解洛谷 P2827 蚯蚓 题解
 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'代码

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 }