bzoj 4553: [Tjoi2016&Heoi2016]序列

Description

 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你
,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可
。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:
1 2 3
2 2 3
1 3 3
1 1 31 2 4
选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列
为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求

Input

 输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的

状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。所有数字均为正整数
,且小于等于100,000

Output

 输出一个整数,表示对应的答案

Sample Input

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

Sample Output

3

HINT

Source

易得方程:

dp[i]=max{dp[j]+1}(j<i&&MAX[j]<=a[i]&&a[j]<=MIN[i])

然后我们发现这是一个三维偏序问题,可以使用CDQ+树状数组来进行优化。。。

设dp[i]表示以i号结尾的最长不下降子序列的长度,对于分治区间 𝑙, 𝑟 ,我们先递归处理编号上的左半部分
[𝑙, 𝑚𝑖𝑑],然后用左半部分更新右半部分。这样的更新方式,保证了左半部分更新右半部分时左半部分解的
最优性(因为右半部分不会对其迚行更新(即满足𝑖 ≥ 𝑗))。对于当前处理的区间,使用归并+树状数组进行更
新。

然而我一下就被lst大佬问得怀疑人生。。。仔细琢磨了一下,大致是这样的。。。

首先在外面把a排序。。。

然后solve(l,r)的操作:
1.把[l,r]按照序号分为两个部分(j<i这一维就解决了)

2.solve(l,mid),递归处理左边,处理完之后左边变为按照MAX增序,右边仍然是按照a排序。。。(这样MAX[j]<=a[i]这一维就解决来)

3.用左边区间[l,mid]的dp值来更新右边[mid+1,r]的dp值,具体做法就是把(a[j]<=MIN[i])这一维丢进树状数组,然后查询。。。

4.solve(mid+1,r),递归处理[mid+1,r];

5.按照MAX归并[l,r]。。。

感觉CDQ一直学得云里雾里放光彩。。。GG。。。这个题真的是细思极恐。。。我可能学了一个假的CDQ。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100050;
struct data{
  int a,Min,Max,id,dp;
}g[N],q[N];
bool cmp(const data &a,const data &b){return a.a<b.a;}
int n,m,tr[N],T=1e5;
void update(int x,int val){
  for(int i=x;i<=T;i+=i&-i) tr[i]=max(tr[i],val);
}
void del(int x){
  for(int i=x;i<=T;i+=i&-i) tr[i]=0;
}
int query(int x){
  int ret=0;
  for(int i=x;i;i-=i&-i) ret=max(ret,tr[i]);
  return ret;
}
void CDQ(int l,int r){
  if(l==r){g[l].dp=max(g[l].dp,1);return;}
  int mid=(l+r)>>1;int tt=l-1,sum=mid;
  for(int i=l;i<=r;i++){
    if(g[i].id<=mid) q[++tt]=g[i];else q[++sum]=g[i];
  }
  for(int i=l;i<=r;i++) g[i]=q[i];CDQ(l,mid);
  for(int i=mid+1,x=l;i<=r;i++){
    for(;g[x].Max<=g[i].a&&x<=mid;x++) update(g[x].a,g[x].dp);
    g[i].dp=max(g[i].dp,query(g[i].Min)+1);
  }
  for(int i=l;i<=mid;i++) del(g[i].a);
  CDQ(mid+1,r);tt=l,sum=mid+1;
  for(int i=l;i<=r;i++){
    if((g[tt].Max<=g[sum].Max&&tt<=mid)||(g[tt].Max>g[sum].Max&&sum>r))
      q[i]=g[tt++];
    else q[i]=g[sum++];
  }
  for(int i=l;i<=r;i++) g[i]=q[i];
}
int main(){
  scanf("%d%d",&n,&m);int x,y,ans=0;
  for(int i=1;i<=n;i++) scanf("%d",&g[i].a),g[i].Min=g[i].a,g[i].Max=g[i].a,g[i].id=i;
  for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),g[x].Min=min(g[x].Min,y),g[x].Max=max(g[x].Max,y);
  sort(g+1,g+1+n,cmp);CDQ(1,n);for(int i=1;i<=n;i++) ans=max(ans,g[i].dp);
  printf("%d\n",ans);
  return 0;
}

  

posted @   qt666  阅读(178)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示