zoukankan      html  css  js  c++  java
  • luoguP2048 [NOI2010]超级钢琴

    https://www.luogu.org/problem/P2048

    题意

    给一个序列A,输出前k大的子区间和 的和

    n , k <= 500000

    -1000 ≤ Ai ≤ 1000,1 ≤ L ≤ R ≤ n

    分析

    在读清题之后很容易想到暴力,用前缀和来算区间和 ,把区间和加入堆中,取出前k大的相加即可

    但是这样会MLE, 超内存

    再次看题,想想自己的暴力,我们知道以第o个音符为左端点的, 合法的子区间有很多, 所以会MLE,但是题目求的是最大的几个区间和,所以我们得到一个贪心策略:每次加入堆的是以o为左端点的最优, 也就是最大的区间和,而不是全加进去,这样就不会MLE了。

    定义三元组(o, l, r) = max(sum[t] - sum[o-1] | t∈[l, r] ) , (o, l, r)表示以o为左端点, 右端点在[l, r]范围内的区间和最大值, 因为sum[o-1]是不变的, 所以我们只需要sum[t]最大即可,静态最大——RMQ。

    但是,我们再想一想贪心的策略, 错啦。 虽然它不能再选最优位置 t了,但是可能还有次优的,前k个大的,区间和:(o, l, t-1)和(o, t+1, r),为了避免重复且不丧失其他较优解(即保证解正确完整),我们仍然要把 (o, l, t - 1)(o, l, t−1) , 扔回堆里面去。显然地,在放回去之前应该保证区间的存在,即 l = t 或 r = t 的情况要进行特判。

    这里把 (o, l, t - 1),(o, t+1, r) 扔会堆里的操作显然需要我们维护位置, 而st表中保存的是值,所以需要动一下脑子, 把st里维护的东西改为位置。此操作是在取出(o, l, r) 之后做的,因为它们没有(o, l, r)优, 而且这样在维护位置之后很方便

    #include<cstdio>
    #include<queue>
    using namespace std;
    #define MAX 500000
    #define ll long long
    
    int sum[MAX], f[MAX][19];
    int n,k, l, r;
    
    void st() {
    	for(int k = 1; (1<<k) <= n; k++) 
    		for(int i = 1; i+(1<<k)-1 <= n; i++) {
    			int x = f[i][k-1] , y = f[i + (1<<(k-1))][k-1];
    			f[i][k] = sum[x] > sum[y] ? x : y;
    		}
    }
    
    int RMQ(int l, int r) {
    	int k = 0;
    	while(1<<(k+1) <= r-l+1) k++;
    	int x = f[l][k], y = f[r-(1<<k)+1][k];
    	return sum[x] > sum[y] ? x : y;
    }
    
    struct node{
    	int o, l, r, t;
    	node(int o = 0, int l = 0, int r = 0) : o(o), l(l), r(r), t(RMQ(l, r)) {} 
    	bool operator < (const node& xxx) const {
    		return sum[t]-sum[o-1] < sum[xxx.t]-sum[xxx.o-1];
    	}
    };
    priority_queue <node> q;
    
    int main() {
    	scanf("%d%d%d%d", &n,&k,&l,&r);
    	for(int i = 1; i <= n; i++) {
    		scanf("%d",&sum[i]);
    		f[i][0] = i;
    		sum[i] += sum[i-1];
    	}
    	st();
    	for(int i = 1; i <= n; i++) 
    		if(i+l-1 <= n) q.push(node(i, i+l-1, min(i+r-1, n) )) ;
    	ll ans = 0;
    	int o, l, r, t;
    	for(int i = 1; i <= k; i++) {
    		o = q.top().o, l = q.top().l, r = q.top().r, t = q.top().t;
    		q.pop() ;
    		ans += (ll)(sum[t]-sum[o-1]);
    		if (l != t) q.push(node(o, l, t - 1)); 
            if (t != r) q.push(node(o, t + 1, r));
    	}
    	printf("%lld", ans);
    }
    
  • 相关阅读:
    ASP.NET Forums 2.0 修改集锦(一)
    在自己的应用程序中显示Windows关于对话框
    ASP.NET Forums 2.0 本地化修改(四)
    Flash对双字节的支持问题
    DotNetNuke 2.1.2安装指南
    ASP.NET Forums 2.0 本地化修改(五) 增加页面Meta标记的keywords和description
    ASP.NET Forums 2.0 本地化修改(三)
    JavaScript利用正则表达式自己写数字判断函数
    hash表基础知识(转载)
    求子数组的最大和
  • 原文地址:https://www.cnblogs.com/tyner/p/11256977.html
Copyright © 2011-2022 走看看