题面传送门

原题题号:Codeforces Gym 101821B

题意:

给出一个排列 \(p\),要你找出一个最长上升子序列(LIS)和一个最长下降子序列(LDS),满足它们没有公共元素。或告知无解。

\(1 \leq n \leq 5 \times 10^5\)。

wxh 太强辣!wxhtxdy!

首先可以发现一个小性质,那就是原序列任意一个 LIS 和 LDS 至多只有 \(1\) 个公共元素。

假设它们有 \(2\) 个公共元素 \(p_i,p_j(i<j)\),由于 \(p_i,p_j\) 同时包含在一个 LIS 中,必有 \(p_i<p_j\)。又因为 \(p_i,p_j\) 同时包含在一个 LDS 中,\(p_i>p_j\),矛盾!

我们预处理出 \(f_i\) 表示包含 \(p_i\) 的 LDS 个数,\(sum\) 表示总的 LDS 个数。由于这些数可能很大,我们可以将它模上一个比较大的数。

由于我们只需构造出一组合法的解,我们的目标就是检验是否存在一个合法的 LIS,然后顺带着找出原序列扣除掉这个 LIS 后得到的序列 \(p'\) 的一个 LDS

我们考虑不合法的 LIS 长啥样,假设这个 LIS 为 \([a_{x_1},a_{x_2},\dots,a_{x_l}]\),因为它不合法,所以不存在与它没有交集的 LDS,也就是所有 LDS 都与它有交集。

而根据之前的性质一个 LIS 和 LDS 至多只有 \(1\) 个公共元素,故 \(f_{x_1}+f_{x_2}+\dots+f_{x_l}=sum\)。

那么怎样找这样一个 LIS 呢?

先用树状数组求出 LIS、LDS 的长度,以及上文提到的 \(f_i,sum\) 的值。

求 LIS 的时候结构体里另外维护四个值 \(m_1,m_2,p_1,p_2\),表示在满足上升子序列的长度最大的情况下,两个不同的 \(f_{x_1}+f_{x_2}+\dots+f_{x_l}\) 的值,以及它们对应的前驱。

如果发现存在一个 LIS 它的 \(f_{x_1}+f_{x_2}+\dots+f_{x_l} \neq sum\),那么直接跳出输出就可以了。

/*
Contest: -
Problem: Codeforces Gym 101821 B
Author: tzc_wk
Time: 2020.10.4
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define y1 y1010101010101
#define y0 y0101010101010
typedef pair<int,int> pii;
typedef long long ll;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
const int MOD=23895631;
inline void add(int &x,int v){
x+=v;if(x>=MOD) x-=MOD;
}
struct numway{
int val,way;
numway(int _val=0,int _way=0){val=_val;way=_way;}
numway operator +(numway x){
numway z=*this;
if(x.val>z.val) z.val=x.val,z.way=0;
if(x.val==z.val) z.way=(z.way+x.way)%MOD;
return z;
}
};
int n=read(),a[500005];
struct bit1{
numway tr[500005];
inline void clear(){
for(int i=1;i<=n;i++)
tr[i].val=tr[i].way=0;
}
inline void modify(int x,numway y){
for(int i=x;i<=n;i+=(i&(-i)))
tr[i]=tr[i]+y;
}
inline numway query(int x){
numway ans(0,1);
for(int i=x;i;i-=(i&(-i)))
ans=ans+tr[i];
return ans;
}
} b1;
struct bit2{
int tr[500005];
inline void clear(){
fill0(tr);
}
inline void modify(int x,int y){
for(int i=x;i<=n;i+=(i&(-i)))
tr[i]=max(tr[i],y);
}
inline int query(int x){
int ans=0;
for(int i=x;i;i-=(i&(-i)))
ans=max(ans,tr[i]);
return ans;
}
} b2;
int lds_len=0,lis_len=0;
numway lds1[500005],lds2[500005];
int f[500005];
struct event{
int val,m1,m2,p1,p2;
event(int _val=0,int _m1=-1,int _m2=-1,int _p1=0,int _p2=0){
val=_val;m1=_m1;m2=_m2;p1=_p1;p2=_p2;
}
friend event operator +(event a,event b){
if(a.val>b.val) return a;
if(a.val<b.val) return b;
if(a.m1==-1) return b;
else if(a.m2==-1){
if(b.m1==-1||b.m1==a.m1) a.m2=b.m2,a.p2=b.p2;
else a.m2=b.m1,a.p2=b.p1;
return a;
}
else return a;
}
};
struct bit3{
event tr[500005];
inline void modify(int x,event v){
for(int i=x;i<=n;i+=(i&(-i)))
tr[i]=tr[i]+v;
}
inline event query(int x){
event ans(0,0,-1,0,0);
for(int i=x;i;i-=(i&(-i)))
ans=ans+tr[i];
return ans;
}
} b3;
event lis[500005];
vector<int> ans_lis,ans_lds;
bool cant[500005];
struct bit4{
pii tr[500005];
inline void modify(int x,pii v){
for(int i=x;i<=n;i+=(i&(-i)))
tr[i]=max(tr[i],v);
}
inline pii query(int x){
pii ans=make_pair(0,0);
for(int i=x;i;i-=(i&(-i)))
ans=max(ans,tr[i]);
return ans;
}
} b4;
pii lls[500005];
inline void dump(int x,int y){
while(x){
ans_lis.pb(x);cant[x]=1;
if(lis[x].m1==y){y=(y-f[x]+MOD)%MOD;x=lis[x].p1;}
else{y=(y-f[x]+MOD)%MOD;x=lis[x].p2;}
}
reverse(all(ans_lis));
printf("%d\n",lis_len);
foreach(it,ans_lis) printf("%d ",*it);printf("\n");
for(int i=n;i>=1;i--){
if(cant[i]) continue;
lls[i]=b4.query(a[i]-1);
lls[i].fi++;
b4.modify(a[i],make_pair(lls[i].fi,i));
}
for(int i=1;i<=n;i++){
if(lls[i].fi==lds_len){
for(int j=i;j;j=lls[j].se){
ans_lds.pb(j);
}
break;
}
}
printf("%d\n",lds_len);
foreach(it,ans_lds) printf("%d ",*it);printf("\n");
}
int main(){
for(int i=1;i<=n;i++) a[i]=read();
b1.clear();
for(int i=1;i<=n;i++){
lds1[i]=b1.query(n-a[i]);lds1[i].val++;
b1.modify(n-a[i]+1,lds1[i]);lds_len=max(lds1[i].val,lds_len);
}
b1.clear();
for(int i=n;i>=1;i--){
lds2[i]=b1.query(a[i]-1);lds2[i].val++;
b1.modify(a[i],lds2[i]);
}
// for(int i=1;i<=n;i++) printf("%d %d %d %d\n",lds1[i].val,lds1[i].way,lds2[i].val,lds2[i].way);
for(int i=1;i<=n;i++){
if(lds1[i].val+lds2[i].val-1==lds_len){
f[i]=1ll*lds1[i].way*lds2[i].way%MOD;
}
// cout<<f[i]<<endl;
}
int sum=0;
for(int i=1;i<=n;i++){
if(lds1[i].val==lds_len)
sum=(sum+lds1[i].way)%MOD;
}
// cout<<sum<<endl;
for(int i=1;i<=n;i++){
int x=b2.query(a[i]-1);
b2.modify(a[i],x+1);
lis_len=max(lis_len,x+1);
}
for(int i=1;i<=n;i++){
lis[i]=b3.query(a[i]-1);
lis[i].val++;
if(lis[i].m1!=-1){
lis[i].m1=(lis[i].m1+f[i])%MOD;
}
if(lis[i].m2!=-1){
lis[i].m2=(lis[i].m2+f[i])%MOD;
}
// printf("%d %d %d %d %d\n",lis[i].val,lis[i].m1,lis[i].m2,lis[i].p1,lis[i].p2);
if(lis[i].val==lis_len){
if(lis[i].m1!=-1&&lis[i].m1!=sum){
dump(i,lis[i].m1);return 0;
}
if(lis[i].m2!=-1&&lis[i].m2!=sum){
dump(i,lis[i].m2);return 0;
}
}
b3.modify(a[i],event(lis[i].val,lis[i].m1,lis[i].m2,(lis[i].m1!=-1)?(i):(0),(lis[i].m2!=-1)?(i):(0)));
}
printf("IMPOSSIBLE\n");
return 0;
}

【2020五校联考NOIP #3】序列的更多相关文章

  1. 【2020五校联考NOIP #6】三格缩进

    题意: 给出 \(n\) 个数 \(a_1,a_2,\dots,a_n\),你要进行 \(m\) 次操作,每次操作有两种类型: \(1\ p\ x\):将 \(a_p\) 改为 \(x\). \(2\ ...

  2. 【2020五校联考NOIP #8】自闭

    题目传送门 题意: 有一个 \(n \times m\) 的矩阵,里面已经填好了 \(k\) 个非负整数. 问是否能在其它 \(n \times m-k\) 个格子里各填上一个非负整数,使得得到的矩阵 ...

  3. 【2020五校联考NOIP #8】狗

    题面传送门 原题题号:Codeforces 883D 题意: 有 \(n\) 个位置,每个位置上要么有一条狗,要么有一根骨头,要么啥都没有. 现在你要给每个狗指定一个方向(朝左或朝右). 朝左的狗可以 ...

  4. 【2020五校联考NOIP #7】道路扩建

    题面传送门 题意: 给出一张 \(n\) 个点 \(m\) 条边的无向图 \(G\),第 \(i\) 条边连接 \(u_i,v_i\) 两个点,权值为 \(w_i\). 你可以进行以下操作一次: 选择 ...

  5. 【2020五校联考NOIP #4】今天的你依旧闪耀

    题面传送门 题意: 对于一个长度为 \(n\)(\(n\) 为偶数)的排列 \(p\),定义一次"变换"后得到的排列 \(p'\) 为: \(p'_i=\begin{cases}p ...

  6. 【2020五校联考NOIP #2】矩阵

    咕咕咕到现在~ 题面传送门 题意: 给出一个 \(n\times n\) 的矩阵 \(A\).要你求有多少个 \(n\times n\) 的矩阵 \(B\) 满足: 每一行都是 \(1\) 到 \(n ...

  7. 【2020五校联考NOIP #7】伟大的卫国战争

    题面传送门 题意: 数轴上有 \(n\) 个点,现在要在它们之间连 \(m\) 条边,第 \(i\) 条边连接 \(a_i,b_i\) 两个点. 现在你要钦定每条边连在数轴的上方还是下方,使得任意两条 ...

  8. 【2020五校联考NOIP #6】最佳观影

    题意: 给出一个 \(k \times k\) 的网格和 \(n\) 次操作.其中 \(k\) 为奇数. 每次操作给出一个数 \(m\).每次你要找出一个三元组 \((x,l,r)\) 使得: \(r ...

  9. 【五校联考1day2】JZOJ2020年8月12日提高组T2 我想大声告诉你

    [五校联考1day2]JZOJ2020年8月12日提高组T2 我想大声告诉你 题目 Description 因为小Y 是知名的白富美,所以自然也有很多的追求者,这一天这些追求者打算进行一次游戏来踢出一 ...

随机推荐

  1. 升级更新 Windows10

    升级更新 Windows10:获取 Windows 更新助手 升级 Windows10,它是先下载 Windows10 系统镜像,然后才升级.在下载完 Windows10 后,升级前,有一步骤会询问: ...

  2. 使用寄存器点亮LED

    1. 项目:使用stm32寄存器点亮LED, 分别点亮红.绿.蓝3个灯. 2. 代码: 只需要编写main.c程序,stm3210x.h程序为空(只需要新建即可). 2.1 点亮绿灯main.c程序 ...

  3. 手把手教你学Dapr - 2. 必须知道的概念

    Sidecar 边车 Dapr API提供Http和gRPC两种通讯方式. 运行方式则可以是容器也可以是进程(Windows开发推荐使用Self Hosted,后续会解释). 这样的好处是与运行环境无 ...

  4. stm32学习笔记之GPIO功能框图分析

    GPIO 是通用输入输出端口的简称,简单来说就是STM32 可控制的引脚,STM32 芯片的GPIO 引脚与外部设备连接起来,从而实现与外部通讯.控制以及数据采集的功能.STM32 芯片的GPIO被分 ...

  5. 矩阵n次幂的计算

    1.归纳法 两大数学归纳法 题目一 2.递推关系 题目一 题目二 3.方阵 题目一 4.矩阵对角化(重点) 题目一 题目二 题目三 题目四 5.矩阵性质(综合) 题目一 题目二 对于副对角线: 题目三

  6. 数组中重复的数字 牛客网 剑指Offer

    数组中重复的数字 牛客网 剑指Offer 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中 ...

  7. cf14C Four Segments(计算几何)

    题意: 给四个线段(两个端点的坐标). 判断这四个线段能否构成一个矩形.(矩形的四条边都平行于X轴或Y轴) 思路: 计算几何 代码: class Point{ public: int x,y; voi ...

  8. hdu 5108 Alexandra and Prime Numbers(水题 / 数论)

    题意: 给一个正整数N,找最小的M,使得N可以整除M,且N/M是质数. 数据范围: There are multiple test cases (no more than 1,000). Each c ...

  9. 议题解析与复现--《Java内存攻击技术漫谈》(二)无文件落地Agent型内存马

    无文件落地Agent型内存马植入 可行性分析 使用jsp写入或者代码执行漏洞,如反序列化等,不需要上传agent Java 动态调试技术原理及实践 - 美团技术团队 (meituan.com) 首先, ...

  10. spark structured-streaming 最全的使用总结

    一.spark structured-streaming  介绍 我们都知道spark streaming  在v2.4.5 之后 就进入了维护阶段,不再有新的大版本出现,而且 spark strea ...