首页 > 其他分享 >CSP模拟4

CSP模拟4

时间:2023-07-25 17:46:10浏览次数:37  
标签:pre int max dp 序列 include CSP 模拟

悲,昨天存本地忘发了,今天又不想写模拟 5 的。

考了四道 ARC 就离谱。

A. LIS to Original Sequence

首先考虑 \(k = 1\),唯一的方案就是倒序输出 \(1\) 到 \(n\)。

我们可以想到,这道题的方法是向已经确定的序列 \(A\) 中插入其他数。

对于一个数 \(x(x < A_i)\),是不能把它放在 \(A_i\) 前的,不然会使最长上升子序列的长度变大。

为了保证字典序最小,我们得把能放在 \(A_i\) 后的最小的数放它后边。

最后要特殊处理 \(A_k\),把剩下没有加入答案序列的部分倒序输出。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 200500;

int n,k;
int a[N],t[N];

int main() {
    cin >> n >> k;
    for(int i = 1;i <= k; i++) {
        cin >> a[i];
        t[a[i]] ++;
    }
    if(k == 1) {
        for(int i = n;i >= 1; i--)
            cout << i << " ";
        return 0;
    }

    int cur = 1;
    while(cur <= n && t[cur])
        cur ++;

    for(int i = 1;i < k; i++) {
        cout << a[i] << " ";

        if(cur < a[i]) {
            t[cur] ++;
            cout << cur << " ";
            cur ++;
        }

        while(cur <= n && t[cur])
            cur ++;
    }

    for(int i = n; i >= 1; i--) {
        if(!t[i] || i == a[k])
            cout << i << " ";
    }
    return 0;
}

B. Unique Subsequence

设 \(pre_i\) 表示在 \(i\) 之前最后一个和 \(i\) 相同的数的位置,\(dp_i\) 表示第 \(i\) 个数为结尾的序列的合法方案数。

对于 \(pre_i = 0\),即在 \(i\) 之前不存在与 \(i\) 相同的数,\(dp_i\) 由 \(\left[ 1,i - 1 \right]\) 转移过来。由于这个数还没有在之前出现过,它本身也是一个合法序列,所以要加 \(1\)。

\[dp_i= \sum_{j=1}^{i-1}dp_j + 1 \]

对于 \(pre_i \neq 0\),即在 \(i\) 之前存在与 \(i\) 相同的数,那么我们考虑 \(\left[ 1,pre_i - 1 \right]\) 这部分,由于 \(i\) 已经在之前出现过了,这部分序列加上 \(i\) 的部分在 \(pre_i\) 已经处理过了,再加的话会导致重复。

考虑 \(\left[ pre_i,i - 1 \right]\) 这部分,对于之前加过的单独的 \(i\),这时候要 \(-1\),但是又因为产生了新序列 \(\left\{ pre_i,i \right\}\),这里要 \(+1\),所以最终不加不减。

\[dp_i=\sum^{i-1}_{j=pre_i}dp_j \]

最后我们用树状数组对区间求和进行优化。

C. Maximize GCD

设 \(a_{max}\) 表示 \(max \left\{ a_1,\dots,a_n \right\}\),\(sum\) 表示 \(a_1 + a_2 + \dots + a_n\)。

一般来说,与其处理 \(x | \gcd(A_1,\dots,A_N)\) ,处理 \(x = \gcd(A_1,\dots,A_N)\) 更加容易。这是因为后者能够被分解为各个元素:\(\forall i,x | A_i\)。

因此,我们将解决下面这个问题而不是原来的问题。

寻找 \(x\) 的最大值,这样就有可能在运算后得到 \(x | \gcd(A_1,\dots,A_N)\)。

假设 \(K\) 足够大,能够使得序列中每一个值都加到 \(a_{max}\),那我们就先把每个值都加到 \(a_{max}\),剩下的操作数再平均分配到每一个元素。

如果 \(K\) 不能使得序列中每一个值都加到 \(a_{max}\),我们能够发现,答案的最大值不超过 \(a_{max}\),也就是不超过 \(3 \times 10^5\)。

这个范围我们完全可以从大到小枚举 \(x\) 的值。

如何枚举 \(x\) 的值?

设 \(k\) 为一个整数,并且计算出对于所有 \(A_i\) 能够满足 \((k - 1)x < A_i \leq kx\) 的操作数。

我们可以计算出一个值域 \(\left(kx - x,kx \right]\) 内 \(A_i\) 的个数和 \(A_i\) 的和。由于是静态的,直接前缀和统计就可以。

这样枚举中找到满足需要的操作次数不大于 \(K\) 的 \(x\) 的最大值。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

#define int long long

const int N = 114514 * 6;

int n,K;

int a[N];

int num[N];
// 桶 
int sum1[N],sum2[N];

long long sum = 0;

int max_num = -114514;

void Input() {
    cin >> n >> K;
    for(int i = 1;i <= n; i++) {
        cin >> a[i];
        num[a[i]] ++;
        max_num = max(max_num,a[i]);
    }

    return ;
}

void Ready() {
    for(int i = 1;i <= 2 * max_num; i++) {// 枚举值域 
        sum1[i] = sum1[i - 1] + num[i];
        sum2[i] = sum2[i - 1] + num[i] * i;
    }

    return ;
}

void Work_0() {
    for(int i = 1;i <= n; ++i)
        sum += (max_num - a[i]);
    return ;
}

signed main() {
    Input();

    Ready();

    Work_0();

    if(sum <= K) {
        cout << max_num + (K - sum) / n;
        return 0;
    }

    for(int x = max_num;x >= 1; x--) {
        sum = 0;

        for(int k = 1;(k - 1) * x <= max_num; k++) {
            // 最后一个区间可能只有一小部分 
            // 所以使左区间小于 max_num
            int tmp = sum1[x * k - 1] - sum1[x * (k - 1)];
            sum += tmp * x * k;
            sum -= sum2[x * k - 1] - sum2[x * (k - 1)];
        }

        if(sum <= K) {
            cout << x;
            return 0;
        }
    }
    return 0;
}

标签:pre,int,max,dp,序列,include,CSP,模拟
From: https://www.cnblogs.com/baijian0212/p/csp-4.html

相关文章

  • uni-app 中模拟器真机运行项目
    1.安装模拟器MuMu 2.配置环境变量找到HbuilderX的安装目录,查找adb.exe文件,复制serve.exe所在文件目录的路径,配置到系统变量的Path中         3.在模拟器中进行配置注意:端口为7555不同模拟器的默认端口是不一样的adb的路径一定是HbuilderX的adb路径,使......
  • 【Python】蒙特卡洛模拟 | PRNG 伪随机数发生器 | LCG 线性同余算法 | 马特赛特旋转算
    ......
  • memcpy/memmove模拟实现
    void*my_memmove(void*dest,constvoid*src,size_tnum){ assert(dest&&src); void*ret=dest; if((char*)dest<(char*)src)//从前向后移 { while(num--) { *(char*)dest=*(char*)src; dest=(char*)dest+1; src=(char*)src+1; } } else......
  • SpringBoot中使用测试框架MockMvc来模拟HTTP请求测试Controller接口
    场景Java中进行单元测试junit.Assert断言、Mockito模拟对象、verify验证模拟结果、Java8中lambda的peek方法使用:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127492361上面讲了开发过程中一些测试方法。如果需要在代码中直接测试某个Controller接口,除了每次启......
  • 在 Arch 配置 i3-wm 终端模拟器 xterm
    在Arch配置i3-wm终端模拟器xterm关于怎么在Arch安装i3-wm可以查看上一篇文章......
  • CSP-J 济南刷题训练营
    Day1:基础算法枚举从可能得集合中一一尝试统计贡献。模拟模拟题目中要求的操作NOIP2014生活大爆炸版石头剪刀布洛谷链接:P1328[NOIP2014提高组]生活大爆炸版石头剪刀布注意到赢了是得\(1\)分,平局和输都是\(0\)分,所以我们直接根据题意打表。intVs[5][5]={{0,0,1,1,......
  • 「赛后总结」20230724 CSP 模拟赛
    「赛后总结」20230724CSP模拟赛点击查看目录目录「赛后总结」20230724CSP模拟赛总结。题解T1最长上升子序列ARC125C思路代码T2独特序列ARC125D思路代码T3最大GCDARC126C思路代码T4连续子段ARC126D思路代码想听歌,想看巨人,但是没有条件。总结。rk1三个首杀,前......
  • 济南CSP-J刷题营集训
    Day1比赛T1方差求和可以用前缀和。求平均值时,特判是否整除而输出结果。求方差,我们直接用他给的公式以分数形式算出结果,维护两个分子和分母,通分相减后特判输出。注意要输出最简分数,所以我们用\(\textgcd\)约分。代码:#include<bits/stdc++.h>#definegtgetchar#define......
  • CSP 模拟 4
    今日推歌:SerenadeinG‘EinekleineNachtmusik’K525-WolfgangAmadeusMozart今天比赛直接搬的ARC125,126的CD题,那这样我也能出模拟赛(但是为什么HZOI2022都不写比赛题解,差评今天被HZOI2023,2024薄纱,我直接退役得了T1最长上升子序列考虑一个明显字典序不是......
  • strncmp/strstr 模拟实现
    constchar*my_strstr(constchar*str1,constchar*str2){ assert(str1&&str2); if(!*str2)//逆反逻辑,非0为真,假假为真 returnstr1; constchar*p1=NULL;//不改变str1和str2 constchar*p2=NULL; constchar*start=str1; while(*start) { p1=start;//p1需......