【agc028E】High Elements(动态规划,线段树,贪心)

题面

AtCoder

你有一个\([1,N]\)的排列\(P\)。

一个长度为\(N\)的字符串\(S\)是好的,当且仅当:

  • 两个序列\(X,Y\)这样构造:

    一开始,令\(X,Y\)都是空的。然后对于每一个\(i=1,2,...,N\),依次考虑每一个\(P_i\),如果\(S_i=0\),那么加入到\(X\)末尾,否则加入到\(Y\)末尾。
  • \(X,Y\)的前缀最大值的个数相等。

现在你要求出一个字典序最小的\(S\)。

题解

显然考虑贪心,只要能够放\(0\)我们就会尽可能的放\(0\),。

首先来证明一个结论,如果一个\(S\)合法,那么必定能够使得\(X,Y\)两个数列中,有一个数列中的前缀最大值全是排列\(P\)中的前缀最大值。

这样子考虑,如果两个数列中都存在不是原本前缀最大值的位置,那么交换这两个位置,因为它们原本不是最大值,现在变成了前缀最大值,意味着前缀中比它大并且在它前面的数一定在另外一个集合中,那么交换之后两个串的前缀最大值的个数都会减少\(1\),那么经过若干次交换之后一定能够使得一个集合中的前缀最大值全是\(P\)中的前缀最大值。

那么我们不妨令\(X\)由原串的前缀最大值构成。

我们假设前\(i-1\)位已经构造完毕,现在考虑第\(i\)位可以放哪里。我们先强制放到一个位置,然后来判断是否合法。

不妨令\(X\)中的前缀最大值个数是\(cnt_X\),\(Y\)中的前缀最大值个数为\(cnt_Y\)。

而\([i,n]\)在\(P\)中原本的前缀最大值的个数是\(Q\),而\(Y\)在接下来的数列中将会有\(k\)个最大值是\(P\)的前缀最大值,则\(X\)中会有\(Q-k\)个。再设\(Y\)中非\(P\)的前缀最大值的前缀最大值个数为\(m\),则要满足条件:

\[cnt_X+(Q-k)=cnt_Y+k+m
\]

化简后得到:

\[2k+m=cnt_X+Q-cnt_Y
\]

其中右边是已知的常量了。

不难发现这个东西就是在搭配\(Y\)的数列,那么把\(P\)中的前缀最大值看成\(2\),其他的值看成\(1\),于是问题变成了我们要在后面找出一个序列,使得其前缀最大值的前缀和是右边的常数。因为如果能够令左边的值为\(x\)的话,那么必定能够得到\(x-2\)(删掉一段后缀一定可以做到)。所以我们只要维护\(x\)为奇数和偶数时能够取到的最大值。

同理也可以反过来,变成搭配\(X\)的数列,一样的进行一次查询。

设\(f[i][0/1]\)表示以\(i\)开头的,权值为偶数/奇数的最大值,转移的时候可以从一段区间转移过来,每次就是区间询问,单点修改,用线段树维护即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX],val[MAX],cnt[MAX];
char s[MAX];
struct SegmentTree
{
#define lson (now<<1)
#define rson (now<<1|1)
int t[MAX<<2];
void Build(int now,int l,int r)
{
if(l==r){t[now]=1-1e9;return;}
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
t[now]=max(t[lson],t[rson]);
}
void Modify(int now,int l,int r,int p,int w)
{
if(l==r){t[now]=w;return;}
int mid=(l+r)>>1;
if(p<=mid)Modify(lson,l,mid,p,w);
else Modify(rson,mid+1,r,p,w);
t[now]=max(t[lson],t[rson]);
}
int Query(int now,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return t[now];
int mid=(l+r)>>1,ret=-1e9;
if(L<=mid)ret=max(ret,Query(lson,l,mid,L,R));
if(R>mid)ret=max(ret,Query(rson,mid+1,r,L,R));
return ret;
}
}Odd,Even;
bool check(int mx,int Q)
{
if(Q<0)return false;
if(Q&1)return Odd.Query(1,1,n,mx,n)>=Q;
else return Even.Query(1,1,n,mx,n)>=Q;
}
int main()
{
n=read();
for(int i=1,mx=0;i<=n;++i)
{
a[i]=read();
if(a[i]>mx)val[i]=2,mx=a[i];
else val[i]=1;
}
Odd.Build(1,1,n);
for(int i=n;i;--i)
{
int mx1=Odd.Query(1,1,n,a[i],n),mx0=Even.Query(1,1,n,a[i],n);
if(val[i]&1)Odd.Modify(1,1,n,a[i],mx0+val[i]),Even.Modify(1,1,n,a[i],mx1+val[i]);
else Odd.Modify(1,1,n,a[i],mx1+val[i]),Even.Modify(1,1,n,a[i],mx0+val[i]);
}
for(int i=n;i;--i)cnt[i]=cnt[i+1]+val[i]-1;
int cntX=0,cntY=0,mxX=0,mxY=0;
for(int i=1;i<=n;++i)
{
Odd.Modify(1,1,n,a[i],1-1e9);Even.Modify(1,1,n,a[i],0);
if(check(mxY,cntX+cnt[i+1]-cntY+(a[i]>mxX)))s[i]='0',cntX+=a[i]>mxX,mxX=max(mxX,a[i]);
else if(check(max(mxX,a[i]),cntY+cnt[i+1]-cntX-(a[i]>mxX)))s[i]='0',cntX+=a[i]>mxX,mxX=max(mxX,a[i]);
else s[i]='1',cntY+=a[i]>mxY,mxY=max(mxY,a[i]);
}
if(cntX!=cntY){puts("-1");}
else printf("%s",s+1);
return 0;
}

【agc028E】High Elements(动态规划,线段树,贪心)的更多相关文章

  1. BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心

    BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心 Description 在计算机中,CPU只能和高速缓存Cache直接交换数据.当所需的内存单元不在Cache中时,则需要从主存里把数 ...

  2. BZOJ_1672_[Usaco2005 Dec]Cleaning Shifts 清理牛棚_动态规划+线段树

    BZOJ_1672_[Usaco2005 Dec]Cleaning Shifts 清理牛棚_动态规划+线段树 题意:  约翰的奶牛们从小娇生惯养,她们无法容忍牛棚里的任何脏东西.约翰发现,如果要使这群 ...

  3. Bzoj5251 线段树+贪心

    Bzoj5251 线段树+贪心 记录本蒟蒻省选后的第一篇题解!国际惯例的题面:首先这个东西显然是一棵树.如果我们把数值排序,并建立这棵树的dfs序,显然dfs序上的一个区间对应数值的一个区间,且根为数 ...

  4. 2018.10.20 NOIP模拟 蛋糕(线段树+贪心/lis)

    传送门 听说是最长反链衍生出的对偶定理就能秒了. 本蒟蒻直接用线段树模拟维护的. 对于第一维排序. 维护第二维的偏序关系可以借助线段树/树状数组维护逆序对的思想建立权值线段树贪心求解. 代码

  5. 2019牛客多校第一场 I Points Division(动态规划+线段树)

    2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...

  6. codeforces 675E Trains and Statistic 线段树+贪心统计

    分析:这个题刚看起来无从下手 但是我们可以先简化问题,首先可以固定起点i,求出i+1到n的最小距离 它可以到达的范围是[i+1,a[i]],贪心的想,我们希望换一次车可以到达的距离尽量远 即:找一个k ...

  7. BZOJ1805[Ioi2007]Sail船帆——线段树+贪心

    题目描述 让我们来建造一艘新的海盗船.船上有 N个旗杆,每根旗杆被分成单位长度的小节.旗杆的长度等于它被分成的小节的数目.每根旗杆上会挂一些帆,每张帆正好占据旗杆上的一个小节.在一根旗杆上的帆可以任意 ...

  8. BZOJ5249 九省联考2018IIIDX(线段树+贪心)

    显然这形成了一个树形结构.考虑这样一种贪心:按照曲目顺序,每次取消其父亲的预留,并选择当前可选择(保证其子树有合法选择且满足预留)的最大值,然后对其子树预留出大于等于他的一些值.这个做法显然是正确的. ...

  9. Codeforces 834D The Bakery - 动态规划 - 线段树

    Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredient ...

  10. BZOJ5249: [2018多省省队联测]IIIDX(线段树 贪心)

    题意 题目链接 Sol 不难发现题目给出的是一个树,其中\(\frac{i}{K}\)是\(i\)的父亲节点 首先,当\(d_i\)互不相同时,一个显然的贪心策略就是优先给编号小的分配较大的权值.可以 ...

随机推荐

  1. 图片转换成base64

    let bgcImage = 'http://192.168.0.83:9080/files/4a9c3056-9b9b-4b41-b8e2-fd9f27023c41.jpg' let image = ...

  2. curl 带 body

    curl -H "Content-Type:plain/text" -X POST -d '<?xml version="1.0" encoding=&q ...

  3. (day67)组件、组件化、组件传参、JS补充(命名转换、for in 、数据转换)、css取消选中和模拟小手

    目录 一.初识组件 (一)概念 (二)特点 二.组件的分类 (一)根组件 (二)局部组件 (三)全局组件 二.数据组件化 三.组件的传参 (一)父传子 (二)子传父 四.JS补充 (一)与html命名 ...

  4. 【一起刷LeetCode】在未排序的数组中找到第 k 个最大的元素

    题目描述 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 ...

  5. koa2 从入门到进阶之路 (六)

    之前的文章我们介绍了一下 koa post提交数据及 koa-bodyparser中间件,本篇文章我们来看一下 koa-static静态资源中间件. 我们在之前的目录想引入外部的 js,css,img ...

  6. vue-cli 引用elementUI打包后文件过大

    解决方案:使用externals引用第三方资源,防止element资源被打包到自己项目中,(总共修改3个页面index.html.webpack.base.conf.js.main.js) 1.修改i ...

  7. ES6-扩展运算符和rest运算符

    es6-扩展运算符和rest运算符 扩展运算符:不确定他的参数个数时使用运算扩展符 // 声明一个方法 但不确定他的参数个数时使用对象运算扩展符 function ananiha(...arg){ c ...

  8. WebGIS小理论(持续更新)

    什么是OWC服务体系 它是开放地理空间协会(Open Geospatial Consortium,OGC)提出的OGC Web服务通用规范. 主要内容: 地理数据服务(Data Service) 对空 ...

  9. Android Gradle 学习笔记(四):Gradle 构建脚本

    本节我们从整体的角度来介绍一下Gradle. 一.setting.gradle 在Gradle中,定义了一个设置文件,用于初始化以及工程树的配置.设置文件的默认的名字就是setting.gradle, ...

  10. iOS常用算法之单链表查找倒数第n个节点(图解)

    拿到题目, 首先要先了解链表数据结构, 如下图: 常规思路: 利用数组, 遍历整个单链表, 将每个节点装入数组中, 最终拿到数组根据索引(数组长度-1-n)就得到了倒数第n个元素, 这里要注意从数组中 ...