博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【agc028E】High Elements(动态规划,线段树,贪心)
阅读量:4558 次
发布时间:2019-06-08

本文共 3385 字,大约阅读时间需要 11 分钟。

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

题面

你有一个\([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
#include
using namespace std;#define MAX 200200inline 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;}

转载于:https://www.cnblogs.com/cjyyb/p/11147938.html

你可能感兴趣的文章
【Luogu1471】方差(线段树)
查看>>
DEV中svg图标的使用
查看>>
Codefroces Gym101572 I.Import Spaghetti-有向图跑最小环输出路径(Floyd)
查看>>
有关位运算的操作+二进制状态压缩
查看>>
Eclipse插件 -- 阿里巴巴扫描编码规插件
查看>>
(1.1)学习笔记之mysql体系结构(内存、进程、线程)
查看>>
markdown测试
查看>>
Java-Maven-Runoob:Maven 依赖管理
查看>>
杂项-Log:log4net
查看>>
杂项-Java:EL表达式
查看>>
tarroni music
查看>>
unity 使用RotateAround的使用注意
查看>>
[SDOI2009]HH的项链
查看>>
CodeFirst模式,容易引发数据迁移问题(不建议使用)
查看>>
jquery的colorbox关闭并传递数据到父窗
查看>>
使用Nginx、Keepalived构建文艺负载均衡
查看>>
phpmyadmin 开放远程登录的权限
查看>>
linux安装gcc和gcc-c++
查看>>
qq登陆错误提示
查看>>
bzoj 1192: [HNOI2006]鬼谷子的钱袋 思维 + 二进制
查看>>