首页 > 其他分享 >[NOIP2016 提高组] 蚯蚓 题解

[NOIP2016 提高组] 蚯蚓 题解

时间:2024-11-21 19:08:12浏览次数:1  
标签:NOIP2016 ch int 题解 px long read heap 蚯蚓

考场思路

考虑要动态维护最大值,可以直接使用优先队列进行维护,但是,考虑到我们并不好直接修改优先队列中的每一个元素,所以决定使用 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

相关文章

  • C语言 蓝桥杯某例题解决方案(查找完数)
    蓝桥杯原题: 一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6=1+2+3.编程找出1000以内的所有完数。这个题没有很大的难点,与我们上一个解决的问题“质因数分解”不同,它不需要判断因数是否是质数,因此我们的工作量会小很多。现在我们的想法还是类似,首先找到......
  • 【Flinkcdc问题解决】java.lang.NoClassDefFoundError: org/apache/flink/shaded/guav
    1.环境介绍Flink1.17+Flinkcdc2.2.12.问题描述使用Flink1.17和Flinkcdc2.2.1环境进行数据加工,但是报以上错误,原因是版本不匹配,flinkcdc2.2.1用的是guava18,但是flink1.17用的是guava30,导致冲突。3.问题解决添加flink-sql-connector-mysql-cdc依赖<dependen......
  • [题解](更新中)2024/11/21 模拟赛 / 2023牛客OI赛前集训营-提高组(第二场) A~B
    整套都是原题所以就不设密码了(原题页面:https://ac.nowcoder.com/acm/contest/65193题解:https://www.nowcoder.com/discuss/540225827162583040\(60+30+20+20=130\)。每日挂分之T2线段树不开\(4\)倍+\(10^6\)数量级输入不关同步流,\(\bf\colorbox{MidnightBlue}{\texttt{\color{......
  • ABC379 题解[A-D]
    ABC379题解目录ABC379题解目录A CyclicB StrawberriesC SowingStonesD HomeGardenE SumofAllSubstringsA Cyclicmanwhatcanisay?#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;usingull=unsignedlonglong;usingld=l......
  • 去水印小程序downloadFile域名问题解决方式
    ......
  • Atcoder Regular Contest 060 题解
    ARC060C.TakandCards*1583简单题。考虑一个非常非常常见的Trick。把区间平均值为\(k\)转化为区间和为\(0\)只需要将每个数都减去\(k\)即可。然后就是一个朴素的背包求和为\(0\)方案数。注意处理负数下标就好了。#include<bits/stdc++.h>usingnamespacestd;typ......
  • 【题解】洛谷P3531: [POI2012] LIT-Letters
    P3531[POI2012]LIT-Letters写了个假做法有点伤心,本以为是两个都求逆序对然后答案相减,但是这样在部分数据上确实合法,但是实际上毫无章法没有逻辑。正解考虑贪心,我们一个数字肯定要找最前面,第二次出现就去最前面第二次出现的位置,因为如果第一个A放在了后面,那么就有可能产生更对......
  • P8290 填树 题解
    题意:给定一棵树,第\(i\)个点的赋值范围是\([L_i,R_i]\)。计数:选择一条路径,将路径上的点赋值,使得极差\(\leK\);并求出每种这样赋值方案的权值和。\(n\le200\),其余\(\le10^9\)。看见极差,考虑枚举最小值\(x\),然后统计\([x,x+k]\)的答案。思路很简单,但是下一个问题是:\(x......
  • Atcoder Regular Contest 059 题解
    ARC059C.BeTogether签到题。枚举要改成哪个,因为值域只有\([-100,100]\)。然后对总代价取个\(\min\)即可。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongLL;constLLMAXN=105;LLn,A[MAXN];intmain(){ ios::sync_with_stdio(false); cin.ti......
  • [题解]CF1685B Linguistics
    @hzjoiineg为什么是神?思路首先将\(S\)中A的数量不等于\(a+c+d\)的情况判掉。然后将\(S\)划分为ABAB...和BABA...的若干段,对于长度为奇数的段构造方案只能是如下构成:A开头为例):AB和BA共\(\lfloor\frac{len}{2}\rfloor\)个,再加一个A。将\(a\)减一,并用......