[USACO18DEC]Sort It Out P 题解
LIS 计数
Statement
[USACO18DEC]Sort It Out P - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
Solution
其实这道题最精髓的一步在于转化题目
我们发现题目中”按顺序排好“这个操作正向模拟非常困难,我们需要寻找更简洁的充要条件
我们直接去看终态,因为终态需要是有序的,我们不妨直接把这个操作理解为把数字 ,放到 号位置
那么这样,判定就变成了选择一个集合,使得集合中的数字放到应该放的位置,形成的序列是否升序
正确性:”按顺序排好“在左边比她小,右边比她大的时候停止,如若集合外的数都在应该在的位置,那么当然会在应该放的位置停止
如若存在有一些数的顺序混乱,显然我的排序操作不会影响他们的相对顺序,所以 GG
也就是说,原来条件中成立的现在条件中也成立,原来条件中不成立的现在也不成立,所以是充要的
容易想到集合外的数应该构成上升子序列,求字典序第 小的满足题意的集合,就是求序列中字典序 第 大的最长上升子序列
沿用普通序列 大的求法,我们应当求出从每一个数开始的 LIS 个数
Luogu 题解区的大佬采用了更为优雅的 DP 方式,即记 表示以值 为开头的 LIS ,然后重定义了一下加法,算出方案数
蒟蒻比较蠢,设 表示以第 个位置为开头的 LIS ,容易用 BIT 求出
然后考虑 表示以第 个位置为开头的 LIS 的方案数怎么算(没有想到可以重定义加法/kk)
枚举 LIS 长度,有这样的式子
有点吓人,不妨把 相同的丢到同一个 vector 里面去,同一个 vector 里面下标单调递增
计算 时,可以二分出要查询的位置挂在 中处理 的条件,然后 BIT 处理 的条件
这样算出来还是 ,容易发现本质和重定义加法然后和 一起算出来的效果相同
开始处理询问,应当从 LIS 大的开始
因为字典序的要求是对 的要求,所以我们把 vec 按 从小到大排序
那么,当我选择了 后,下一个数 应满足 ,用两个变量限制一下就可以了
总复杂度
Code
#include<bits/stdc++.h>
#define lowbit(x) (-x&x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e5+5;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
ll read(){
ll s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
struct BITmx{
int c[N];
void add(int x,int v){for(;x<N;x+=lowbit(x))c[x]=max(c[x],v);}
int ask(int x){int r=0;for(;x;x-=lowbit(x))r=max(r,c[x]);return r;}
}mx;
struct BITsum{
ll c[N];
void clear(int x){for(;x<N;x+=lowbit(x))c[x]=0;}
void add(int x,ll v){for(;x<N;x+=lowbit(x))c[x]=min((ull)1e18,(ull)c[x]+v);}
ll ask(int x){ll r=0;for(;x;x-=lowbit(x))r=min((ull)1e18,(ull)r+c[x]);return r;}
//因为方案数可能爆 long long ,所以这里要和 1e18 取 min,由于运算过程中还是有可能爆 long long ,所以干脆 unsigned 一下 (调了 1h+/kk)
}sum;
int a[N],b[N],f[N],pos[N],n,m;
vector<int>vec[N],item[N];
ll g[N],k;
bool vis[N];
signed main(){
n=read(),k=read();
for(int i=1;i<=n;++i)
a[i]=read(),b[a[i]]=i;
for(int i=n;i>=1;--i)
f[i]=mx.ask(n-a[i]+1)+1,
mx.add(n-a[i]+1,f[i]),m=max(m,f[i]);
for(int i=1;i<=n;++i)
vec[f[i]].push_back(i);
// for(int i=1;i<=m;++i,puts(""))
// for(auto v:vec[i])cout<<v<<" ";
for(auto v:vec[1])g[v]=1;
for(int i=2;i<=m;++i){
for(auto v:vec[i])
pos[v]=upper_bound(vec[i-1].begin(),vec[i-1].end(),v)-vec[i-1].begin(),
item[pos[v]].push_back(v);
for(int j=vec[i-1].size()-1;~j;--j){
sum.add(n-a[vec[i-1][j]]+1,g[vec[i-1][j]]);
for(auto v:item[j])g[v]=sum.ask(n-a[v]+1);
}
for(auto v:vec[i-1])
sum.clear(n-a[v]+1);
for(auto v:vec[i])
if(item[pos[v]].size())
item[pos[v]].clear();
}
// for(int i=1;i<=n;++i)cout<<g[i]<<" ";puts("");
// for(int i=1;i<=m;++i,puts(""))
// for(auto v:vec[i])cout<<pos[v]<<" ";
for(int i=1;i<=m;++i)
sort(vec[i].begin(),vec[i].end(),[](int x,int y){
return a[x]<a[y];
});
int lim1=0,lim2=0;
for(int i=m;i;--i){
for(int j=vec[i].size()-1;~j;--j){
if(vec[i][j]>lim1&&a[vec[i][j]]>lim2){
if(g[vec[i][j]]<k)
k-=g[vec[i][j]];
else{
vis[a[vec[i][j]]]=1;
lim1=vec[i][j],lim2=a[vec[i][j]];
break;
}
}
}
}
printf("%d\n",n-m);
for(int i=1;i<=n;++i)
if(!vis[i])printf("%d\n",i);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】