1 条题解
-
0
P6795 题解
题目大意
给定一个 阶排列 的前 位,确定整个排列使得值域连续段数最大,给出构造。
数据范围:。
思路分析
设 离散化后得到 。
考虑分类:
- 若左右端点都在 :直接线段树或析合树统计即可。
- 若左右端点都在 :可以证明剩余元素的每个值域连续段都是按顺序填在一起的,即每个都不在 中的值域区间在序列上一定连续。
否则考虑一个前后缀拼合的形态:,显然一个必要条件是 是值域连续段。
考虑取出所有 的值域连续段,按 大到小排序,设这些区间原本的值域是 ,第 个区间对应的后缀是 。
不妨设当前区间为 ,考虑从 转移的过程:显然会降序加入 ,升序加入 。
不妨设在 中:值为 的数的前驱是 ,后继是 ,因此我们考虑 这个区间的贡献,注意到如果有多个 相等,那么这个区间可以选一个最优的时刻下放。
考虑在当前时刻下放的贡献 :
- 若 显然 。
- 否则考虑左端点从 移动到 的过程,注意到指针扫过的 这个区间,这些数的值域是 ,如果这个值域里的数都在 中,那么 。
- 否则显然 ,考虑什么时候 ,那么我们要求 中不在 中的元素一定紧接着 ,具体可以看代码:维护 排序后的值域区间,要求 是 所在值域区间的上一个值域区间里的最大值。
最终维护每个段最优的下放贡献以及转移点,容易求出答案和构造方案。
代码呈现
#include<bits/stdc++.h> #define ll long long using namespace std; const int MAXN=2e5+5,inf=1e9; int n,m; struct SegmentTree { struct Node { int add,min,cnt; } tr[MAXN<<2]; inline int L(int p) { return p<<1; } inline int R(int p) { return p<<1|1; } inline void psu(int p) { tr[p].cnt=0,tr[p].min=std::min(tr[L(p)].min,tr[R(p)].min); if(tr[L(p)].min==tr[p].min) tr[p].cnt+=tr[L(p)].cnt; if(tr[R(p)].min==tr[p].min) tr[p].cnt+=tr[R(p)].cnt; } inline void adt(int p,int k) { tr[p].add+=k,tr[p].min+=k; } inline void psd(int p) { adt(L(p),tr[p].add),adt(R(p),tr[p].add),tr[p].add=0; } inline void bd(int l=1,int r=n,int p=1) { tr[p].cnt=r-l+1; if(l==r) return ; int mid=(l+r)>>1; bd(l,mid,L(p)),bd(mid+1,r,R(p)); } inline void add(int ul,int ur,int k,int l=1,int r=n,int p=1) { if(ul<=l&&r<=ur) return adt(p,k); int mid=(l+r)>>1; psd(p); if(ul<=mid) add(ul,ur,k,l,mid,L(p)); if(mid<ur) add(ul,ur,k,mid+1,r,R(p)); psu(p); } } T; int sn[MAXN],tn,sx[MAXN],tx; int a[MAXN],b[MAXN],rk[MAXN],id[MAXN],L[MAXN],R[MAXN]; int fl[MAXN],fr[MAXN],gl[MAXN],gr[MAXN]; inline void pl(int x) { for(int i=b[x]-1;i>=b[x-1]+1;--i) printf("%d ",i); } inline void pr(int x) { for(int i=b[x]+1;i<=b[x+1]-1;++i) printf("%d ",i); } signed main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) scanf("%d",&a[i]),b[i]=a[i]; ll ans=0; T.bd(),T.add(1,n,inf); for(int i=1;i<=m;++i) { T.add(1,i,-1),T.add(i,i,-inf+1); while(tn&&a[sn[tn]]>a[i]) T.add(sn[tn-1]+1,sn[tn],a[sn[tn]]),--tn; sn[++tn]=i,T.add(sn[tn-1]+1,sn[tn],-a[i]); while(tx&&a[sx[tx]]<a[i]) T.add(sx[tx-1]+1,sx[tx],-a[sx[tx]]),--tx; sx[++tx]=i,T.add(sx[tx-1]+1,sx[tx],a[i]); if(!T.tr[1].min&&i<m) ans+=T.tr[1].cnt; } sort(b+1,b+m+1),b[m+1]=n+1; for(int i=1;i<=m;++i) rk[b[i]]=i; for(int i=0;i<=m;++i) ans+=1ll*(b[i+1]-b[i]-1)*(b[i+1]-b[i])/2; for(int i=1,c=0;i<=m;++i) { if(i==1||b[i]>b[i-1]+1) id[i]=++c,L[c]=R[c]=i; else R[c]=i,id[i]=c; } for(int i=m,l=n+1,r=0,kl=n+1,kr=0,wl=0,wr=0;i;--i) { l=min(l,rk[a[i]]),r=max(r,rk[a[i]]); if(r-l==m-i) { if(l<kl) wl=0; else if(id[r]!=id[kr]) wl=(kr==R[id[r]-1]); if(r>kr) wr=0; else if(id[l]!=id[kl]) wr=(kl==L[id[l]+1]); ++wl,++wr; if(wl>fl[l]) fl[l]=wl,gl[l]=i; if(wr>fr[r]) fr[r]=wr,gr[r]=i; kl=l,kr=r,++ans; } } for(int i=1;i<=m;++i) ans+=1ll*fl[i]*(b[i]-b[i-1]-1)+1ll*fr[i]*(b[i+1]-b[i]-1); printf("%lld\n",ans); for(int i=1;i<=m;++i) printf("%d ",a[i]); for(int i=m,l=n+1,r=0,kl=n+1,kr=0;i;--i) { l=min(l,rk[a[i]]),r=max(r,rk[a[i]]); if(r-l==m-i) { if(i<m) { for(int j=kl-1;j>=l+1;--j) pl(j); for(int j=kr+1;j<=r-1;++j) pr(j); } kl=l,kr=r; int zl=(gl[l]==i)?fl[l]:0,zr=(gr[r]==i)?fr[r]:0; if(zl>zr) { if(zl) pl(l); if(zr) pr(r); } else { if(zr) pr(r); if(zl) pl(l); } } } puts(""); return 0; }
- 1
信息
- ID
- 2437
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 6
- 已通过
- 1
- 上传者