[POI2007] KLO

太nb了这题。佩服波兰人的脑洞。正解实在没学会,学了另外一种解法。不过似乎本质相同。

首先考虑设 f ( i , j ) f(i,j) f(i,j) 表示前 i i i 个积木里面保存了 j j j 个的方案数。

转移 f ( i , j ) = max ⁡ { f ( i − 1 , j ) , f ( i − 1 , j − 1 ) + [ a i = j ] } f(i,j)=\max\{f(i-1,j),f(i-1,j-1)+[a_i=j] \} f(i,j)=max{f(i1,j),f(i1,j1)+[ai=j]}

这个东西维护非常困难。正解就是给出了一种转平面然后逆斜二测,对角线维护的方法。

我们考虑另一种本质相同的解法。转换状态。

f ( i ) f(i) f(i) 表示强制 a i a_i ai i i i 上,最多有多少积木满足条件。

转移: f ( i ) = max ⁡ j < i , a j < a i , a i − a j ≤ i − j { f ( j ) + 1 } f(i)=\max\limits_{j<i,a_j<a_i,a_i-a_j\le i-j} \{f(j)+1 \} f(i)=j<i,aj<ai,aiajijmax{f(j)+1}

按照 i − a i i-a_i iai 排序,然后树状数组维护值域上的前缀最大值即可。时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

// Problem: P6564 [POI2007] 堆积木KLO
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6564
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define fir first
#define sec second
using pii = std::pair<int, int>;

const int maxn = 1e5 + 5;
int n, c[maxn];
pii a[maxn];

int lowbit(int x) {
	return x & -x;
}

void add(int x, int y) {
	for(;x <= n;x += lowbit(x))
		c[x] = std::max(c[x], y);
	return ;
}

int query(int x) {
	int ans = 0;
	for(;x;x -= lowbit(x))
		ans = std::max(ans, c[x]);
	return ans;
}

int main() {
	scanf("%d", &n);
	for(int i = 1;i <= n;++ i)
		scanf("%d", &a[i].sec), a[i].fir = i;
	std::sort(a + 1, a + 1 + n, [&](const pii& p, const pii& q) {
		return (p.fir - p.sec == q.fir - q.sec) ? (p.fir < q.fir) : (p.fir - p.sec < q.fir - q.sec);
	});
	for(int i = 1;i <= n;++ i) {
		if(a[i].sec > a[i].fir)
			continue ;
		add(a[i].sec, query(a[i].sec - 1) + 1);
	}
	printf("%d\n", query(n));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值