[NOIP2016 提高组] 蚯蚓 题解

考场思路

考虑要动态维护最大值,可以直接使用优先队列进行维护,但是,考虑到我们并不好直接修改优先队列中的每一个元素,所以决定使用 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 ;
}
posted @ 2024-11-21 19:17  Torrentolf  阅读(45)  评论(0)    收藏  举报