CSP-S模拟9
A. 最长上升子序列
考虑构造字典序最小的方案,他长这个样子
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >='0');
return x;
}
const int maxn = 200005;
int n, k, a[maxn], ans[maxn], rem[maxn];
bool vis[maxn];
int main(){
n = read(); k = read();
for(int i = 1; i <= k; ++i)vis[a[i] = read()] = true;
if(k == 1){
for(int i = n; i >= 1; --i)printf("%d ",i);
return 0;
}
int p = 1;
for(int i = 1; i <= n; ++i)if(!vis[i]){
while(p <= k - 1 && (a[p] < i || rem[p]))++p;
if(p >= k)break;
rem[p] = i; vis[i] = 1;
}
p = 0;
for(int i = 1; i <= k - 1; ++i){
ans[++p] = a[i];
if(rem[i])ans[++p] = rem[i];
}
for(int i = n; i >= a[k]; --i)ans[++p] = i;
for(int i = a[k]; i > 0; --i)if(!vis[i])ans[++p] = i;
for(int i = 1; i <= n; ++i)printf("%d ",ans[i]);
return 0;
}
B. 独特序列
考虑设 表示以 为结尾的合法子序列个数
表示上一个与 相同的位置
那么
考虑到 直接到后面的转移就不合法了,所以把 清除
用 简单维护即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >='0');
return x;
}
const int maxn = 200005;
const int mod = 998244353;
int a[maxn], n, pre[maxn], vis[maxn];
struct BIT{
int t[maxn];
int lowbit(int x){return x & -x;}
void add(int x, int val){
val = (val % mod + mod) % mod;
while(x <= n + 1){
t[x] += val;
t[x] = t[x] >= mod ? t[x] - mod : t[x];
x += lowbit(x);
}
}
int query(int x){
int ans = 0;
while(x){
ans = (ans + t[x]) % mod;
x -= lowbit(x);
}
return ans;
}
}t;
int main(){
n = read();
for(int i = 1; i <= n; ++i)a[i] = read();
int ans = 0;
a[0] = 0; a[n + 1] = n + 1;
for(int i = 1; i <= n; ++i){
pre[i] = vis[a[i]];
vis[a[i]] = i;
}
for(int i = 1; i <= n; ++i)vis[a[i]] = 0;
for(int i = 1; i <= n; ++i){
int now = t.query(i) -(pre[i] ? t.query(pre[i] - 1) : 0);
if(vis[a[i]] == 0){
vis[a[i]] = i;
++now;
}else{
t.add(vis[a[i]], t.query(vis[a[i]] - 1)- t.query(vis[a[i]]));
vis[a[i]] = i;
}
now = now < 0 ? now + mod : now;
t.add(i, now);
}
printf("%d\n",t.query(n + 1));
return 0;
}
C. 最大GCD
随机化过的,这里就不写出来影响大家了
正解其实是暴力 所有可能值,优化了
考虑在值域上进行 ,假设 的为 , 每次取 计算,前缀和即可
这样出来复杂度为调和级数,近似
D. 连续子段
装压记录状态
每次考虑是否选择该位置的元素
如果选择计算逆序对数即为贡献
不选择,那么左侧/右侧的序列需要移动拼起来,那么就是左右元素个数的
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >='0');
return x;
}
const int maxn = 205;
const int mk = 66000;
const int inf = 0x3f3f3f3f;
int n, k, a[maxn];
int f[2][mk], cnt[mk];
int vis[21];
void ckmin(int &x, int y){x = min(x, y);}
int main(){
n = read(), k = read();
for(int i = 1; i <= n; ++i)a[i] = read();
int mx = 1 << k;
for(int i = 1; i < mx; ++i)cnt[i] = cnt[i - (i & -i)] + 1;
for(int i = 0; i <= 1; ++i)
for(int j = 1; j < mx; ++j)
f[i][j] = inf;
int ans = inf;
for(int i = 0; i < n; ++i){
int nt = i & 1, nzt = 1 - nt;
f[nt][0] = 0;
for(int j = 0; j < mx; ++j){
if(f[nt][j] != inf){
ckmin(f[nzt][j], f[nt][j] + min(cnt[j], k - cnt[j]));
if(a[i + 1] <= k && (j & (1 << (a[i + 1] - 1))) == 0)ckmin(f[nzt][j | (1 << (a[i + 1] - 1))], f[nt][j] + cnt[j >> a[i + 1]]);
f[nt][j] = inf;
}
}
ans = min(ans, f[nzt][mx - 1]);
}
printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】