CCC '19 S5 - Triangle: The Data Structure(倍增 + 滑窗最大值)

题目链接
感谢emofunx提供解题思路和耐心答疑!

题目大意:

给你一个高度为n的数字金字塔,求它的所有高度为k的子金字塔的最大值的总和。 ( 1 ≤ k ≤ n ≤ 3000 ) (1\le k \le n \le 3000) (1kn3000)
在这里插入图片描述
样例输入:

4 2
3
1 2
4 2 1
6 1 4 2

样例输出:

23
解题思路:

对于一个可以移动的固定大小区间内的区间,我们可以想到用滑窗去解决,但是这是二维的,而且还是三角形,咋滑啊?
这时候emofunx说道:其实这类问题的处理都差不多,就是可能麻烦点。
注意到一个大的三角形,是可以由多个小的三角形组成的,这时候我们可以想倍增:预处理出所有高为2^i的三角形的最值。那么 2 i 2^i 2i高度的三角形如何推出 2 i + 1 2^{i+1} 2i+1的三角形呢,看图:
在这里插入图片描述
如图,可以用三个 2 i 2^i 2i高度的三角形拼出1个中空的 2 i + 1 2^{i+1} 2i+1的三角形,因为有空洞,我们不能直接递推。但是我们可以发现:
在这里插入图片描述
假设当前三角形左下角为 ( x , y ) (x,y) (x,y)红色这部分的面积可以被左下角坐标为 ( x , y ) 到 ( x + 2 i , y ) (x,y)到(x+2^i, y) (x,y)(x+2i,y),高度为 2 i 2^i 2i的这 2 i + 1 2^i+1 2i+1个小三角形完全覆盖。
所以我们可以用滑窗法维护一段区间长度固定的数字的最值来转移维护出倍增数组。最后也可以用滑窗来利用倍增数组得到每个大小为k的子金字塔的最大值,把它们都加到答案里就完事儿了。
ac代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e3 + 50;
int f[2][maxn][maxn], a[maxn][maxn];
int n, k;
int q[maxn], head, tail;
int main()
{
	cin>>n>>k;
	for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= i; ++j) scanf("%d", &a[i][j]), f[0][i][j] = a[i][j];
	} ll ans = 0;
	if(k == 1){// 特判
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= i; ++j) ans += a[i][j];
        }cout<<ans<<endl; return 0;
	}
	int cur = 0;
	for(int t = 1; ; ++t){
        cur ^= 1;//滚动倍增数组
        int len, l;//需要获取的三角形大小,滑窗的大小

        if((1<<t) < k) len = (1<<t), l = len>>1;
        else len = k, l = k - (1<<(t-1));
        l++;
        for(int i = len; i <= n; ++i){
            head = tail = 0;
            int o = 1;
            for(int j = 1; j + len - 1 <= i; ++j){
                while(o <= j + l - 1){//滑窗获取区间最值
                    while(tail > head && f[cur^1][i][q[tail-1]] <= f[cur^1][i][o]) tail--;
                    q[tail++] = o++;
                }
                f[cur][i][j] = max(f[cur^1][i][q[head]], f[cur^1][i-l+1][j]);
                if(len == k) ans = ans + (ll) f[cur][i][j];
                if(q[head] == j) head++;
            }
        }
        if(len == k) break;
	}
	cout<<ans<<endl;
}
/*
4 3
3
1 2
2 1 3
4 1 1 1
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值