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 还在研究中
随机推荐
- 在Vue&Element前端项目中,对于字典列表的显示处理
在很多项目开发中,我们为了使用方便,一般都会封装一些自定义组件来简化界面的显示处理,例如参照字典的下拉列表显示,是我们项目中经常用到的功能之一,本篇随笔介绍在Vue&Element前端项目中如 ...
- Java---String和StringBuffer类
Java---String和StringBuffer类 Java String 类 字符串在Java中属于对象,Java提供String类来创建和操作字符串. 创建字符串 创建字符串常用的方法如下: ...
- props&attrs provide inject
defineComponent({ props: {// 1 } setup (props, {attrs, emit}) { } }) 一,组件传值: 父传子: 1.如果没有在定义的props中声明 ...
- Scrum Meeting 14
第14次例会报告 日期:2021年06月07日 会议主要内容概述: 汇报了已完成的工作,明确了下一步目标,正在努力赶进度. 一.进度情况 我们采用日报的形式记录每个人的具体进度,链接Home · Wi ...
- UltraSoft - Beta - Scrum Meeting 1
Date: May 17th, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 维护Beta阶段文档 Liuzh 前端 增加删除操作按钮 Kkkk 前端 查询增加 ...
- 面试不再慌,终于有人把TCP讲明白了。。。
前言 TCP(Transmission Control Protocol,传输控制协议) 是计算机网络的的重要组成部分,也是网络编程的重要内容,还有我们平时接触最多的 HTTP 也是基于 TCP 实现 ...
- Noip模拟40 2021.8.15
T1 送花 按照题解意思说是扫描线题,但我觉得像一个线段树优化$dp$ 主要思想一样,就是暴力枚举右端点,同时维护左端点的最值, 考虑两种情况, 如果左端点在$r$扫到的数$i$上一次出现的位置之前, ...
- 在c中使用正则表达式
今天学习编译原理的时候,用c写一个简易的文法识别器实验遇到了一个问题:要用正则表达式去识别正则文法里面的A->ω,A->Bω, 其中ω属于T的正闭包,也就是说我们对正则文法的产生式进行抽象 ...
- hdu 1159 Common Subsequence(最长公共子序列,DP)
题意: 两个字符串,判断最长公共子序列的长度. 思路: 直接看代码,,注意边界处理 代码: char s1[505], s2[505]; int dp[505][505]; int main(){ w ...
- 并发编程从零开始(十四)-Executors工具类
并发编程从零开始(十四)-Executors工具类 12 Executors工具类 concurrent包提供了Executors工具类,利用它可以创建各种不同类型的线程池 12.1 四种对比 单线程 ...