【USACO 2018 December Contest, Platinum Problem 2】Sort It Out [JZOJ100129] 排序【DP】【线段树】

Description

以下是翻译魔改版题面:

FJ有N条胖头鱼(分别用1…N编号)排成一行。
FJ喜欢他的胖头鱼以升序排列,不幸的是现在他们的顺序被打乱了。
在过去FJ曾经使用一些诸如“冒泡排序”的开创性的算法来使他的胖头鱼排好序,但今天他想偷个懒。
取而代之,他会每次对着一条胖头鱼叫道“按顺序排好”。
当一条胖头鱼被叫到的时候,他会确保自己在队伍中的顺序是正确的(从他的角度看来)。
只要有一条紧接在他右边的胖头鱼的编号比他小,他们就交换位置。
然后,只要有一头紧接在他左边的胖头鱼的编号比她大,他们就交换位置。
这样这条胖头鱼就完成了“按顺序排好”,在这条胖头鱼看来左边的胖头鱼编号比他小,右边的胖头鱼编号比他大。
FJ想要选出这些胖头鱼的一个子集,然后遍历这个子集,依次对着每一条胖头鱼发号施令(按编号递增的顺序),重复这样直到所有N条胖头鱼排好顺序。
例如,如果他选出了编号为{2,4,5}的胖头鱼的子集,那么他会喊叫胖头鱼2,然后是胖头鱼4,然后是胖头鱼5。
如果N条胖头鱼此时仍未排好顺序他会再次对着这几条胖头鱼喊叫,如果有必要的话继续重复。
由于FJ不确定哪些胖头鱼比较专心,他想要使得这个子集最小。此外,他认为K是个幸运数字。请帮他求出满足重复喊叫可以使得所有胖头鱼排好顺序的最小子集之中字典序第K小的子集。
我们称{1,…,N}的一个子集S在字典序下小于子集T,当S的所有元素组成的序列(按升序排列)在字典序下小于T的所有元素组成的序列(按升序排列)。例如,{1,3,6} 在字典序下小于{1,4,5}。

N<=10^5 ,K<=10^18

Solution

既然我们要选择一个子集,这里面的数可以动,那么也就意味着删除这个子集中的数后剩下的部分相对顺序不能改变,也就是要求要是递增的。

要求删去的自己最小,就是让剩下的部分最大,就是最长上升子序列。
求字典序第K小,那么剩下的部分字典序就是第K大。
问题转化为求字典序第K大的最长上升子序列。

我们需要求出两个值 f [ i ] , g [ i ] f[i],g[i] f[i],g[i],分别表示以位置i开头的最长上升子序列的长度以及数量。
f [ i ] f[i] f[i]是经典做法, g [ i ] g[i] g[i]可以用树状数组来做(树状数组维护一个f,g的二元组),也可以无脑上线段树

接下来只需要一位一位确定就行了,把所有位置按f[i]分组,对于同一组的排序以后扫过去确定就好。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define LL long long
using namespace std;
int n,f[N],a[N],d[N],rt[N],n1,t[40*N][2],df[N];
LL m,g[N],sm[40*N];
vector<int> dc[N];
bool bz[N];
bool cmp(int x,int y)
{
	return x>y;
}
bool cmp2(int x,int y)
{
	return a[x]<a[y];
}
void nwp(int &x) {if(!x) x=++n1;}
void ins(int k,int l,int r,int x,LL v)
{
	if(l==r&&l==x) {sm[k]+=v;if(sm[k]>m+1) sm[k]=m+1;return;}
	int mid=(l+r)>>1;
	if(x<=mid) nwp(t[k][0]),ins(t[k][0],l,mid,x,v);
	else nwp(t[k][1]),ins(t[k][1],mid+1,r,x,v);
	sm[k]=sm[t[k][0]]+sm[t[k][1]];
	if(sm[k]>m+1) sm[k]=m+1;
}
LL query(int k,int l,int r,int x,int y)
{
	if(!k||!sm[k]||x>y||x>r||y<l) return 0;
	if(x<=l&&r<=y) return sm[k];
	int mid=(l+r)>>1;
	return query(t[k][0],l,mid,x,y)+query(t[k][1],mid+1,r,x,y);
}
int main()
{
	cin>>n>>m;
	fo(i,1,n) scanf("%d",&a[i]);
	int l=0;
	d[0]=n+1;
	fod(i,n,1)
	{
		int w=upper_bound(d,d+l+1,a[i],cmp)-d;
		if(w>l) rt[++l]=++n1;
		d[w]=max(d[w],a[i]);
		f[i]=w,g[i]=(w==1)?1:query(rt[w-1],1,n,a[i]+1,n);
		dc[w].push_back(i);
		ins(rt[w],1,n,a[i],g[i]);
	}
	int ml=0;
	fod(i,l,1)
	{
		int r=dc[i].size();
		d[0]=0;
		fo(j,0,r-1) if(a[dc[i][j]]>a[ml]&&dc[i][j]>ml) d[++d[0]]=dc[i][j];
 		sort(d+1,d+d[0]+1,cmp2);
		while(g[d[d[0]]]<m) m-=g[d[d[0]]],--d[0];
		bz[ml=d[d[0]]]=1; 
	}
	memset(d,0,sizeof(d));
	fo(i,1,n) if(!bz[i]) d[++d[0]]=a[i];
	sort(d+1,d+n-l+1);
	printf("%d\n",n-l);
	fo(i,1,n-l) printf("%d\n",d[i]);
}
已标记关键词 清除标记
相关推荐
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页