【2020五校联考NOIP #3】序列
题面传送门
原题题号: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】序列的更多相关文章
- 【2020五校联考NOIP #6】三格缩进
题意: 给出 \(n\) 个数 \(a_1,a_2,\dots,a_n\),你要进行 \(m\) 次操作,每次操作有两种类型: \(1\ p\ x\):将 \(a_p\) 改为 \(x\). \(2\ ...
- 【2020五校联考NOIP #8】自闭
题目传送门 题意: 有一个 \(n \times m\) 的矩阵,里面已经填好了 \(k\) 个非负整数. 问是否能在其它 \(n \times m-k\) 个格子里各填上一个非负整数,使得得到的矩阵 ...
- 【2020五校联考NOIP #8】狗
题面传送门 原题题号:Codeforces 883D 题意: 有 \(n\) 个位置,每个位置上要么有一条狗,要么有一根骨头,要么啥都没有. 现在你要给每个狗指定一个方向(朝左或朝右). 朝左的狗可以 ...
- 【2020五校联考NOIP #7】道路扩建
题面传送门 题意: 给出一张 \(n\) 个点 \(m\) 条边的无向图 \(G\),第 \(i\) 条边连接 \(u_i,v_i\) 两个点,权值为 \(w_i\). 你可以进行以下操作一次: 选择 ...
- 【2020五校联考NOIP #4】今天的你依旧闪耀
题面传送门 题意: 对于一个长度为 \(n\)(\(n\) 为偶数)的排列 \(p\),定义一次"变换"后得到的排列 \(p'\) 为: \(p'_i=\begin{cases}p ...
- 【2020五校联考NOIP #2】矩阵
咕咕咕到现在~ 题面传送门 题意: 给出一个 \(n\times n\) 的矩阵 \(A\).要你求有多少个 \(n\times n\) 的矩阵 \(B\) 满足: 每一行都是 \(1\) 到 \(n ...
- 【2020五校联考NOIP #7】伟大的卫国战争
题面传送门 题意: 数轴上有 \(n\) 个点,现在要在它们之间连 \(m\) 条边,第 \(i\) 条边连接 \(a_i,b_i\) 两个点. 现在你要钦定每条边连在数轴的上方还是下方,使得任意两条 ...
- 【2020五校联考NOIP #6】最佳观影
题意: 给出一个 \(k \times k\) 的网格和 \(n\) 次操作.其中 \(k\) 为奇数. 每次操作给出一个数 \(m\).每次你要找出一个三元组 \((x,l,r)\) 使得: \(r ...
- 【五校联考1day2】JZOJ2020年8月12日提高组T2 我想大声告诉你
[五校联考1day2]JZOJ2020年8月12日提高组T2 我想大声告诉你 题目 Description 因为小Y 是知名的白富美,所以自然也有很多的追求者,这一天这些追求者打算进行一次游戏来踢出一 ...
随机推荐
- html视口单位:vw,vh,rem
前言 不像响应式布局,通过media query,设置几个变化点来适配,流体排版通过调整大小,适配所有设备宽度.这个方法可以使我们开发的网页,在几乎所有屏幕尺寸上都可以使用.但出于一些原因,它的使用率 ...
- java的加载与执行原理详解
java程序从开发到最终运行经历了什么? (31) 编译期: 第一步:在硬盘某个位置(随意),新建一个xxx.java文件 第二步:使用记事本或者其他文本编辑器例如EditPlus打开xxx.java ...
- 【原创】Linux v4l2框架分析
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 第三次Scrum Metting
日期:2021年4月27日会议主要内容概述:确定后端和前端接口,前端讨论画图页面,解决两处画图问题 一.进度情况# 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇龙 后端 ...
- 线程池系列二:一张动图,彻底懂了execute和submit
我们知道线程池通过execute方法执行提交的Runnable任务,但Runnable只是执行任务,没有返回任何信息. [线程池原理:线程池原来是个外包公司,打工人我悟了] 若是我们想在异步执行完 ...
- 玩转C语言链表-链表各类操作详解
链表概述 链表是一种常见的重要的数据结构.它是动态地进行存储分配的一种结构.它可以根据需要开辟内存单元.链表有一个"头指针"变量,以head表示,它存放一个地址.该地址指向一个元素 ...
- Ubuntu 16.04 下 旋转显示器屏幕 竖屏显示
xrandr -o left $ xrandr -o left 向左旋转90度 $ xrandr -o right 向右旋转90度 $ xrandr -o inverted 上下翻转 $ xrandr ...
- C/C++如何传递二维数组?
用二维数组作为参数传递(用二维数组处理矩阵),但是希望接受传递二维数组参数的函数可以处理任意维度的数组(希望矩阵的行数和列数都是不固定的). ----------------------------- ...
- 面试官问我JVM内存结构,我真的是
面试官:今天来聊聊JVM的内存结构吧? 候选者:嗯,好的 候选者:前几次面试的时候也提到了:class文件会被类加载器装载至JVM中,并且JVM会负责程序「运行时」的「内存管理」 候选者:而JVM的内 ...
- JavaScript复习 1
概括及使用方法: JavaScript编写规范 一般放在<head>-</head>中间 逐行被执行,越短越好 大小写敏感 语句是基本单位 通常以分号表示语句结束 多行语句可以 ...