Codeforces 650D - Zip-line(树状数组)
我怕不是个 nt……一开始忽略了”询问独立“这个条件……然后就一直在想有什么办法维护全局 LIS……心态爆炸
首先离散化。预处理出以每个点为结尾的 LIS 长度 \(f_i\),以及以每个点为开头的 LIS 长度 \(g_i\)。
不难发现每次只修改一个元素,故每次询问的答案只可能是原序列 LIS 的长度 \(mx\pm 1\)。
我们不妨来探究什么情况下询问的答案为 \(mx+1\),什么情况下询问的答案为 \(mx-1\)。
\(+1\) 的情况比较容易,只可能是存在一个通过 \(a_i\) 的长度为 \(mx+1\) 的上升序列,如果我们记 \(f'_i\) 为将 \(a_i\) 位置上的值换成 \(b_i\) 后,以 \(a_i\) 结尾的 LIS 的长度,\(g'_i\) 为将 \(a_i\) 位置上的值换成 \(b_i\) 后,以 \(a_i\) 开头的 LIS 的长度,那么新序列中经过 \(a_i\) 的上升序列的长度最大值即为 \(f'_i+g'_i-1\),故若 \(f'_i+g'_i-1=mx+1\),则新的 LIS 长度为 \(mx+1\)。
\(-1\) 的情况相对来说比较困难,新序列 LIS 的长度为 \(mx-1\) 需要两个条件,一是经过 \(a_i\) 的 LIS 长度 \(<mx\),二是所有长度为 \(mx\) 的上升子序列都经过 \(a_i\)。条件一比较容易检验,还是记 \(f'_i\) 为将 \(a_i\) 位置上的值换成 \(b_i\) 后,以 \(a_i\) 结尾的 LIS 的长度,那么经过 \(a_i\) 的 LIS 长度就是 \(f'_i+g'_i-1\)。比较麻烦的是条件二,一种可能的处理方式是在求 \(f_i\) 的同时求出 \(ed_i\) 表示有多少个长度为 \(f_i\) 的上升子序列以 \(i\) 结尾,\(st_i\) 表示有多少个长度为 \(g_i\) 的上升子序列以 \(i\) 开头,那么总共有 \(\dfrac{1}{mx}\sum\limits_{i}st_ied_i[f_i+g_i-1=mx]\) 个长度为 \(mx\) 的 LIS。检验是否所有长度为 \(mx\) 的上升子序列都经过 \(a_i\) 需要满足两个条件,一是 \(f_{a_i}+g_{a_i}-1=mx\),二是经过 \(a_i\) 的 LIS 的个数等于长度为 \(mx\) 的 LIS 的总个数,即 \(st_ied_i=\dfrac{1}{mx}\sum\limits_{i}st_ied_i[f_i+g_i-1=mx]\),由于 LIS 的个数很多,故这里的 \(f_i,g_i\) 需模上一个大质数,如 \(998244353\) 等,这个实现起来略有些困难,就不展开讲解了(估计 CF 上此题 hashing 的 tag 就是留给这个解法的罢)。
这里给出一个较为简便的做法,首先 \(f_{a_i}+g_{a_i}-1=mx\) 是必要条件,如果 \(f_{a_i}+g_{a_i}-1\neq mx\) 那肯定不满足条件,其次关于 LIS 有一个性质,那就是若 \(f_i+g_i-1=mx\),对于所有经过 \(i\) 的 LIS,该 LIS 中第 \(f_i\) 大的元素一定是 \(i\)。考虑反证法,设 \(i\) 是这样的 LIS 中第 \(j\) 个元素,若 \(j<f_i\),那么在 \(i\) 后面的元素有 \(mx-j>g_i\) 个,而根据 \(g_i\) 的定义知以 \(i\) 开头的 LIS 长度最大为 \(g_i\),矛盾,\(j>f_i\) 的情况也同理。考虑对于不经过 \(i\) 的 LIS,这样的 LIS 中第 \(f_i\) 大的元素是什么,根据之前的分析知假设第 \(f_i\) 大的元素是 \(j\),那么一定有 \(f_j=f_i\)。也就是说如果 \(f_j=f_{a_i},j\neq a_i\) 的 \(j\) 存在,那 \(a_i\) 就不符合题意。故只需开一个桶 \(c_i\) 表示有多少 \(f_j=i\) 的 \(j\) 并检验 \(c_{f_{a_i}}=1\) 即可。
至于怎么求 \(f_i,g_i\)……就按照套路把询问挂在 \(a_i\) 处,然后按照树状数组求 LIS 的套路扫描一遍即可。时间复杂度 \(n\log n\)。
那问题就来了,如果这题询问不独立怎么做呢?
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=4e5;
int n,qu,key[MAXN*2+5],uni[MAXN*2+5],num=0,cnt=0;
int a[MAXN+5],x[MAXN+5],y[MAXN+5];
int v[MAXN+5],nxt[MAXN+5],hd[MAXN+5],item_n=0;
void ins(int p,int q){v[++item_n]=q;nxt[item_n]=hd[p];hd[p]=item_n;}
int getnum(int x){
int l=1,r=num;
while(l<=r){
int mid=(l+r)>>1;
if(uni[mid]==x) return mid;
if(uni[mid]<x) l=mid+1;
else r=mid-1;
}
}
int f[MAXN+5],g[MAXN+5],qf[MAXN+5],qg[MAXN+5],c[MAXN+5];
int t[MAXN*2+5];
void add(int x,int v){for(int i=x;i<=num;i+=(i&(-i))) chkmax(t[i],v);}
int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) chkmax(ret,t[i]);return ret;}
int main(){
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),key[++cnt]=a[i];
for(int i=1;i<=qu;i++) scanf("%d%d",&x[i],&y[i]),ins(x[i],i),key[++cnt]=y[i];
sort(key+1,key+cnt+1);
for(int i=1;i<=cnt;i++) if(key[i]!=key[i-1]) uni[++num]=key[i];
for(int i=1;i<=n;i++) a[i]=getnum(a[i]);
for(int i=1;i<=qu;i++) y[i]=getnum(y[i]);
for(int i=1;i<=n;i++) f[i]=query(a[i]-1)+1,add(a[i],f[i]);
memset(t,0,sizeof(t));
for(int i=n;i;i--) g[i]=query(num-a[i])+1,add(num-a[i]+1,g[i]);
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++){
for(int e=hd[i];e;e=nxt[e]){
int id=v[e];qf[id]=query(y[id]-1)+1;
} add(a[i],f[i]);
}
memset(t,0,sizeof(t));
for(int i=n;i;i--){
for(int e=hd[i];e;e=nxt[e]){
int id=v[e];qg[id]=query(num-y[id])+1;
} add(num-a[i]+1,g[i]);
}
int mx=0;
for(int i=1;i<=n;i++) chkmax(mx,f[i]+g[i]-1);
for(int i=1;i<=n;i++) if(f[i]+g[i]-1==mx) c[f[i]]++;
for(int i=1;i<=qu;i++){
if(qf[i]+qg[i]-1>mx) printf("%d\n",qf[i]+qg[i]-1);
else if(qf[i]+qg[i]-1<mx&&f[x[i]]+g[x[i]]-1==mx&&c[f[x[i]]]==1) printf("%d\n",mx-1);
else printf("%d\n",mx);
}
return 0;
}
Codeforces 650D - Zip-line(树状数组)的更多相关文章
- [Codeforces 1208D]Restore Permutation (树状数组)
[Codeforces 1208D]Restore Permutation (树状数组) 题面 有一个长度为n的排列a.对于每个元素i,\(s_i\)表示\(\sum_{j=1,a_j<a_i} ...
- Codeforces 830B - Cards Sorting 树状数组
B. Cards Sorting time limit per test 1 second memory limit per test 256 megabytes input standard inp ...
- codeforces 589G G. Hiring(树状数组+二分)
题目链接: G. Hiring time limit per test 4 seconds memory limit per test 512 megabytes input standard inp ...
- CodeForces–830B--模拟,树状数组||线段树
B. Cards Sorting time limit per test 1 second memory limit per test 256 megabytes input standard inp ...
- Codeforces 1139F Dish Shopping 树状数组套平衡树 || 平衡树
Dish Shopping 将每个物品拆成p 和 s 再加上人排序. 然后问题就变成了, 对于一个线段(L - R), 问有多少个(li, ri)满足 L >= li && R ...
- CodeForces 522D Closest Equals 树状数组
题意: 给出一个序列\(A\),有若干询问. 每次询问某个区间中值相等且距离最短的两个数,输出该距离,没有则输出-1. 分析: 令\(pre_i = max\{j| A_j = A_i, j < ...
- Codeforces 960F Pathwalks ( LIS && 树状数组 )
题意 : 给出若干个边,每条边按照给出的顺序编号,问你找到一条最长的边权以及边的编号同时严格升序的一条路径,要使得这条路径包含的边尽可能多,最后输出边的条数 分析 : 这题和 LIS 很相似,不同的 ...
- CodeForces - 597C Subsequences (树状数组+动态规划)
For the given sequence with n different elements find the number of increasing subsequences with k + ...
- codeforces Gym100589H Count Subarrays 树状数组/线段树+离散化
题意:给你一个数组,问你有多少子数组中的逆元数不小于K个,N<105 还在研究中
随机推荐
- Golang通脉之包的管理
在工程化的开发项目中,Go语言的源码复用是建立在包(package)基础之上的. 包(package)是多个Go源码的集合,是一种高级的代码复用方案,Go语言提供了很多内置包,如fmt.os.io等. ...
- noj加1乘2平方
广度优先搜索典例 00 题目 描述: 最简单的队列的使用#include <iostream>#include <queue>using namespace std;queue ...
- Linux argc,argv详解
来源:微信公众号「编程学习基地」 @ 目录 argc,argv是什么 如何解析程序参数 "选项"是什么? "选项字符串"是什么 解析参数 argc,argv是什 ...
- 第二次Scrum Metting
日期:2021年4月25日会议主要内容概述:前后端针对WebAPI进行协调与统一工作,商量接下来两日计划:敲定部分设计细节. 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中 ...
- Prometheus重新标记
Prometheus重新标记 一.背景 二.简化的指标抓取的生命周期 1.配置参数详解 1.`action:`存在的值 1.替换标签值 2.删除指标 3.创建或删除标签 2.删除标签注意事项 3.几个 ...
- IDEA注释设置:从当前鼠标位置开始注释快捷键
在写xml或html注释时,经常出现注释出来的时候都是顶格的,前面包含一大段空格,并没有在鼠标位置开始. 可在设置中进行修改,其他代码格式修改方法类似
- 2021.8.3考试总结[NOIP模拟29]
T1 最长不下降子序列 数据范围$1e18$很不妙,但模数$d$只有$150$,考虑从这里突破. 计算的式子是个二次函数,结果只与上一个值有关,而模$d$情况下值最多只有$150$个,就证明序列会出现 ...
- 算法:汉诺塔问题(Tower of Brahma puzzle)
一.算法背景 最早发明这个问题的人是法国数学家爱德华·卢卡斯.传说越南河内某间寺院有三根银棒(A, B, C),上串 64 个金盘. 寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子:预言说当这 ...
- 电路维修(双端队列 & 最短路)
达达是来自异世界的魔女,她在漫无目的地四处漂流的时候,遇到了善良的少女翰翰,从而被收留在地球上. 翰翰的家里有一辆飞行车. 有一天飞行车的电路板突然出现了故障,导致无法启动. 电路板的整体结构是一个$ ...
- ICPC Mid-Central USA Region 2019 题解
队友牛逼!带我超神!蒟蒻的我还是一点一点的整理题吧... Dragon Ball I 这个题算是比较裸的题目吧....学过图论的大概都知道应该怎么做.题目要求找到七个龙珠的最小距离.很明显就是7个龙珠 ...