Jzoj P5920 风筝___模拟+LIS+map

206 篇文章 0 订阅
127 篇文章 0 订阅

题目大意:

一个有 n n n个数的序列,有 Q Q Q次修改,每次改变某个数的数值,求整个序列的 L I S LIS LIS,修改间互相不影响。

n , m ≤ 5 e 5 n,m≤5e5 n,m5e5

分析:

对于每次的操作 a [ x ] = y a[x]=y a[x]=y,我们可以进行排序然后离线操作,
对于某次操作 a [ x ] = y a[x]=y a[x]=y
我们求出当 a [ x ] = y a[x]=y a[x]=y时,
y y y结尾的 1 1 1 x x x的最长严格上升子序列,设为 L [ i ] L[i] L[i]
y y y开头的 x x x n n n的最长严格上升子序列,设为 R [ i ] R[i] R[i]
然后我们求出原序列 a a a a l [ i ] al[i] al[i] a r [ i ] ar[i] ar[i],设原序列的最长严格上升子序列长度为 k k k
我们可以发现,
L [ i ] + R [ i ] − 1 > = k L[i]+R[i]-1 >= k L[i]+R[i]1>=k时,就代表了修改后的位置 a [ x ] a[x] a[x]对于答案有影响的,而此时的最长严格上升子序列长度则为 L [ i ] + R [ i ] − 1 L[i]+R[i]-1 L[i]+R[i]1
a l [ i ] + a r [ i ] − 1 = k al[i]+ar[i]-1=k al[i]+ar[i]1=k时,
我们可以发现,如果 a [ x ] a[x] a[x]存在于原序列的 L I S LIS LIS中的第 c c c位,那么对于原序列的多个 L I S LIS LIS,它都是在第 c c c位的,那么我们只需要判断第 c c c位置的选择数目即可,如果不为1,那么就代表存在相等作用的数能够代替它发挥作用,此时我们的答案就是 k k k,否则就是 k − 1 k-1 k1
求的过程经过某博客指点发现可以用离散xjb乱求

代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#include <map>
#define inf 0x3f3f3f3f
#define N 500005

using namespace std;
 
struct Node { int x, y, id, lnum, rnum; }C[N];
int Answer[N], Check[N], lisl[N], lisr[N], a[N], f[N], len, cnt, n, m;	
 
void read(int &x)
{
	int f = 1; x = 0; char s = getchar();
	while (s < '0' || s > '9') { if ( s == '-') f = -1; s = getchar(); }
	while (s >= '0' && s <= '9') { x = x * 10 + (s - '0'); s = getchar(); }
	x = x * f;
}

void write(int x)
{
    if (x > 9) write(x / 10);
	putchar(x % 10 + '0');	
}

bool cmp(Node aa, Node bb)
{
    return aa.x < bb.x;	
}
 
int main()
{
    freopen("kite.in", "r", stdin);
    freopen("kite.out", "w", stdout);
	read(n); read(m);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i <= m; i++) read(C[i].x), read(C[i].y), C[i].id = i;
	sort(C + 1, C + m + 1, cmp);
	
	len = 0; cnt = 1;
	for (int i = 1; i <= n; i++)  f[i] = inf;
	
	for (int i = 1; i <= n; i++) 
	{
		while (cnt <= m && C[cnt].x == i)
		{
			int pos = lower_bound(f + 1, f + n + 1, C[cnt].y) - f;
			C[cnt++].lnum = pos; 
		}
		int pos = lower_bound(f + 1, f + n + 1, a[i]) - f;
		lisl[i] = pos; f[pos] = a[i]; len = max(len, pos);
	}
    
	cnt = m;
	for (int i = 1; i <= n; i++) f[i] = inf;
	for (int i = n; i >= 1; i--) 
	{
		while (cnt >= 1 && C[cnt].x == i)
		 {
			int pos = lower_bound(f + 1, f + n + 1, -C[cnt].y) - f;
			C[cnt--].rnum = pos;
		}
		int pos = lower_bound(f + 1, f + n + 1, -a[i]) - f;
		lisr[i] = pos; f[pos] = -a[i];
	}
	
	for (int i = 1; i <= n; i++) 
		if (lisl[i] + lisr[i] > len) Check[lisl[i]]++;
	
	for (int i = 1; i <= m; i++)
		if (C[i].lnum + C[i].rnum > len) Answer[C[i].id] = C[i].lnum + C[i].rnum - 1;
		   else if (lisl[C[i].x] + lisr[C[i].x] > len && Check[lisl[C[i].x]] == 1) Answer[C[i].id] = len - 1;
		           else Answer[C[i].id] = len;
	for (int i = 1; i <= m; i++) write(Answer[i]), printf("\n");
    return 0;
}
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值