考场思路
考虑要动态维护最大值,可以直接使用优先队列进行维护,但是,考虑到我们并不好直接修改优先队列中的每一个元素,所以决定使用 vector 先排一遍序,再使用冒泡排序进行动态维护,时间复杂度\(O(mn)\),可以拿 35pts 。
代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std ;
int n = 0 , m = 0 , q = 0 , u = 0 , v = 0 , t = 0 ;
vector<int> a ;
inline int read()
{
int w = 0 ;
char ch = getchar() ;
while(!isdigit(ch))
ch = getchar() ;
while(isdigit(ch))
w = (w << 1) + (w << 3) + (ch ^ 48) , ch = getchar() ;
return w ;
}
inline void write(int x)
{
if(x > 9)
write(x / 10) ;
putchar(x % 10 + '0') ;
}
bool cmp(int x , int y)
{
return x > y ;
}
void add(int x)
{
a.push_back(x) ;
int i = a.size() - 1 ;
while(i > 0 && x > a[i - 1])
swap(a[i] , a[i - 1]) , --i ;
}
int main()
{
freopen("earthworm.in" , "r" , stdin) ;
freopen("earthworm.out" , "w" , stdout) ;
n = read() , m = read() , q = read() , u = read() , v = read() , t = read() ;
for(int i = 1 ; i <= n ; ++i)
{
int x = read() ;
a.push_back(x) ;
}
sort(a.begin() , a.end() , cmp) ;
for(int i = 1 ; i <= m ; ++i)
{
int x = a[0] ;
a.erase(a.begin()) ;
if(i % t == 0)
write(x) , putchar(' ') ;
int c = u * x / v ;
int d = x - c ;
for(int j = 0 ; (unsigned)j < a.size() ; ++j)
a[j] += q ;
add(c) ;
add(d) ;
}
putchar('\n') ;
for(int i = t ; (unsigned)i <= a.size() ; i += t)
write(a[i - 1]) , putchar(' ') ;
putchar('\n') ;
fclose(stdin) ;
fclose(stdout) ;
return 0 ;
}
优化思路
考虑到我们在进行整体修改时十分浪费时间,而且冒泡排序维护一次复杂度为\(O(n)\),所以程序效率较低。我们考虑可以从修改入手进行优化。
我们发现其实如果所有蚯蚓都没有被砍的话,它们最后的长度都是初始长度加上\(m*q\),所以,我们其实并不需要在过程中直接进行修改,而是每碰到一个被砍的蚯蚓就将它们当前值多减去一个\(q\),把砍成的两半都减去\(i*q\)当作初始就存在的状态,在最后统计时再进行处理。
所以,此时,我们就可以使用优先队列直接动态维护当前最大值,时间复杂度\(O(m \log m)\),可以拿80pts (本地)或85pts (洛谷)。(当然,如果手写堆就都是85pts)
下面提供我的手写堆代码
代码
#include<iostream>
#include<queue>
using namespace std ;
int n = 0 , m = 0 , q = 0 , u = 0 , v = 0 , t = 0 ;
//priority_queue<long long> a ;
long long heap[10000005] = {} ;
int tot = 0 ;
void push(long long x)
{
++tot ;
heap[tot] = x ;
int i = tot ;
int j = i / 2 ;
while ((j > 0) && heap[j] < heap[i] )
{
swap(heap[j], heap[i]) ;
i = i / 2 ;
j = i / 2 ;
}
}
long long top()
{
return heap[1] ;
}
void pop()
{
heap[1] = heap[tot] ;
--tot ;
int i = 1 ;
int j = i * 2 ;
if (j + 1 <= tot && heap[j + 1] > heap[j])
++j ;
while (j <= tot && heap[j] > heap[i])
{
swap(heap[j] , heap[i]) ;
i = j ;
j = i * 2 ;
if (j + 1 <= tot && heap[j + 1] > heap[j])
++j ;
}
}
bool empty()
{
return tot == 0 ;
}
inline int read()
{
int w = 0 ;
char ch = getchar() ;
while(!isdigit(ch))
ch = getchar() ;
while(isdigit(ch))
w = (w << 1) + (w << 3) + (ch ^ 48) , ch = getchar() ;
return w ;
}
inline void write(long long x)
{
if(x > 9)
write(x / 10) ;
putchar(x % 10 + '0') ;
}
bool cmp(int x , int y)
{
return x > y ;
}
signed main()
{
freopen("earthworm.in" , "r" , stdin) ;
freopen("earthworm.out" , "w" , stdout) ;
n = read() , m = read() , q = read() , u = read() , v = read() , t = read() ;
for(int i = 1 ; i <= n ; ++i)
{
int x = read() ;
// a.push(x) ;
push(x) ;
}
for(int i = 1 ; i <= m ; ++i)
{
// long long x = a.top() ;
// a.pop() ;
long long x = top() ;
pop() ;
x = x + (i - 1) * q ;
if(i % t == 0)
write(x) , putchar(' ') ;
long long c = u * x / v ;
long long d = x - c ;
c -= i * q , d -= i * q ;
// a.push(c) , a.push(d) ;
push(c) , push(d) ;
}
putchar('\n') ;
for(int i = 1 ; !empty() ; ++i)
{
if(i % t == 0)
write(top() + m * q) , putchar(' ') ;
pop() ;
}
putchar('\n') ;
fclose(stdin) ;
fclose(stdout) ;
return 0 ;
}
最终思路
考虑到这题中13、14、20三个点的数据实在太大,\(O(m\log m)\)的复杂度下差不多是刚好超过 1 秒,无法完全通过这道题,所以我们考虑进一步将我们上面的方法的复杂度优化成\(O(m)\)的,这样才能通过这道题。
我们发现发现从大到小取 x,切开形成的 \(\lfloor px \rfloor\) 显然也是从大到小的(正比例函数和 \(\lfloor x \rfloor\) 均单调不降)。那么 \(x-\lfloor px \rfloor\) 呢?
严格证明一下。命题:对于\(x_1,x_2 \in Z,x_1 \geq x_2,0<p<1,有x_1-\lfloor px_1 \rfloor \geq x_2-\lfloor px_2 \rfloor\)。
证明:\(x_1 \geq x_2 且 x_1,x_2 \in Z,因此x_1-x_2 \in N。又因为0<p<1,所以:\)
\(x_1-x_2 \geq p(x_1-x_2)\)
\(x_1-x_2+px_1 \geq px_1\)
\(\lfloor px_2+(x_1-x_2) \rfloor \geq \lfloor px_1 \rfloor\)
\(\lfloor px_2 \rfloor + (x_1-x_2) \geq \lfloor px_1 \rfloor\)
\(x_1-\lfloor px_1 \rfloor \geq x_2-\lfloor px_2 \rfloor\)
因此,我们可以考虑使用三个队列 A,B,C 分别维护初始蚯蚓序列,前半截蚯蚓序列,后半截蚯蚓序列,因为根据上面的证明,我们的 A,B,C 三个队列总保持不升的单调性,每秒维护的时间复杂度\(O(1)\),总的复杂度就降为了\(O(m)\),完全可以通过此题(记得分数乘法是会超 int 所以要记得开long long)
代码
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std ;
#define int long long
queue<int> A , B , C ;
int n = 0 , m = 0 , q = 0 , u = 0 , v = 0 , t = 0 ;
int input[100005];
inline int read()
{
int w = 0 ;
char ch = getchar() ;
while(!isdigit(ch))
ch = getchar() ;
while(isdigit(ch))
w = (w << 1) + (w << 3) + (ch ^ 48) , ch = getchar() ;
return w ;
}
inline void write(int x)
{
if(x > 9)
write(x / 10) ;
putchar(x % 10 + '0') ;
}
bool cmp(int x , int y)
{
return x > y ;
}
signed main()
{
freopen("earthworm.in" , "r" , stdin) ;
freopen("earthworm.out" , "w" , stdout) ;
n = read() , m = read() , q = read() , u = read() , v = read() , t = read() ;
for(int i = 1 ; i <= n ; ++i)
input[i] = read() ;
sort(input + 1 , input + n + 1 , cmp) ;
for(int i = 1 ; i <= n ; ++i)
A.push(input[i]) ;
for(int i = 1 ; i <= m ; ++i)
{
int a = INT_MIN , b = INT_MIN , c = INT_MIN ;
if(!A.empty())
a = A.front() ;
if(!B.empty())
b = B.front() ;
if(!C.empty())
c = C.front() ;
int maxn = max(max(a , b) , c) ;
if(a == maxn)
A.pop() ;
else if(b == maxn)
B.pop() ;
else
C.pop() ;
maxn = maxn + (i - 1) * q ;
if(i % t == 0)
write(maxn) , putchar(' ') ;
int x = u * maxn / v ;
int y = maxn - x ;
x -= i * q , y -= i * q ;
B.push(x) ;
C.push(y) ;
}
putchar('\n') ;
int i = 1 ;
while(!A.empty() || !B.empty() || !C.empty())
{
int a = INT_MIN , b = INT_MIN , c = INT_MIN ;
if(!A.empty())
a = A.front() ;
if(!B.empty())
b = B.front() ;
if(!C.empty())
c = C.front() ;
int maxn = max(max(a , b) , c) ;
if(a == maxn)
A.pop() ;
else if(b == maxn)
B.pop() ;
else
C.pop() ;
if(i % t == 0)
write(maxn + m * q) , putchar(' ') ;
++i ;
}
putchar('\n') ;
fclose(stdin) ;
fclose(stdout) ;
return 0 ;
}
标签:NOIP2016,ch,int,题解,px,long,read,heap,蚯蚓
From: https://www.cnblogs.com/Torrentolf/p/18561360