zoukankan      html  css  js  c++  java
  • CSP-S day3

    今天又是爆零的一天(大雾)

    T1

    序列

    【题目背景】

    参加完 IOI2020 之后就是姚班面试。而你,由于讨厌物理、并且想成为乔布斯一样的创业家,被成功踢回贵系。转眼,时间的指针被指向 2023,大二, 12 月初,考试周。你早听学长说, 离散数学期中考很难,对竞赛生不友好,集训队选手做不完卷子。

    你冷笑。哼,堂堂国际金,这点难度的考试算什么。

    两小时,你看完习题解析前五章所有内容,并且倒背如流;

    一小时,你看了 500 页的讲义,并且记忆犹新;

    十分钟,你骑车到考场,自信的你只带了一把水笔,虽然考试让带资料;

    现在,摊开传说中神级卷子,你定神一看——

    【题目描述】

    你有一个递推式为An = ( ∑ j,n-1  Aj × j )% n,其中A1 = 1
    现在给出 n,你需要求出An
    的值

    【输入描述】

    一行,一个正整数 n
    【输出描述】

    一行,一个整数,表示An
    【样例输入 1

    2
    【样例输出 1

    1
    【数据范围】

    此题共 10 个测试点。
    对于前 10%的数据, n<=1
    对于前 40%的数据, n ≤ 10
    对于前 60%的数据, n ≤ 5000
    对于 100%的数据, 1 ≤ n ≤ 105
    【后记】

    “奋战两小时,考个四五十” 的表情包占领了你的朋友圈:

    • “啊,感觉自己人生完全了”

    • “但愿. . . . . .我真的能拿到四五十”

    • “我考完了. . . . . .考完了. . . . . .完了”

    • “曾经以为是开玩笑的,原来我还是 naïve 了”

    你冷笑。提前半小时交卷,你自然觉得, 离散数学,满分,正常

    这题我爆了!!!!爆了!!!!

    打了1e5的表,表炸了

    事实告诉我们

     防表防爆防骗=毒瘤题
    我的1e5的表啊
     
    然鹅这题的思路其实很简单
    因为手膜我们可以得到
    a[i]*i并不大,用long long就可以存下,然后再进行求值,时间复杂度是O(n)
     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 
     5 int n;
     6 int read()
     7 {
     8     int f=0,num;char ch;
     9     while(ch=getchar(),!isdigit(ch)) if(ch=='-') f=1;num=ch-'0';
    10     while(ch=getchar(), isdigit(ch)) num=num*10+ch-'0';
    11     return f?-num:num;
    12 }
    13 int main()
    14 {
    15     freopen("seq.in","r",stdin);
    16     freopen("seq.out","w",stdout);
    17     n=read();
    18     ll ans=1,a=1;
    19     for(int i=2;i<=n;i++)
    20     {
    21         a=ans%i;
    22         ans=ans+a*i;
    23     }
    24     printf("%lld",a);
    25     fclose(stdin);
    26     fclose(stdout);
    27     return 0;
    28 }

    T2

    片段划分


    【题目背景】

    研表究明,汉的字序顺并不定一能影阅响读。科学家们对数列进行了类似的研究。

    【题目描述】

      给一个正整数数列,若数列首项为数列中所有数的最小值,末项为数列中的最大值,则我们称这是个正确的数列。例如,序列[1,3,2,4][1,2,1,2]是正确的,但序列[1,3,2]不是。给出长度为 n 的序列[ 1 ,, n]。对于该序列的某个片段[ , 1 ,若该片段的首项为该片段中的最小值,末项为该片段中的最大值,则我们称这是个正确的片段。对于给定的序列,请求出该序列至少需要被分成多少段,才能使得每个片段均为正确的片段。序列[2,3,1,1,5,1]可以分为三个正确的段:[2,3][1,1,5][1]。需要编写一个程序,该程序按给定的顺序确定可以划分的最小正确段数。

    【输入格式】

      第一行一个正整数 n 表示序列长度接下来一行 n 个正整数 1 ,表示序列

    【输出格式】

      一个整数表示按照给定顺序可以划分的最小正确段数。

    【样例输入】

     5

      5 4 3 2 1

    【样例输出】

      5

    数据范围】

      本题共 3 Subtask
      对于 100%的数据,保证1 ≤ n ≤ 3 × 1e5,1 ≤a[i] ≤ 1e9
      Subtask1(30pts)n ≤ 500
      Subtask2(30pts)n ≤ 5000
      Subtask3(40pts):无特殊性质

     

    看到这道题就感觉是一道区间DP,最近也有在练来着,然鹅依然不会写,只好写了个O(n^3)的大大大暴力,30分滚粗

    正解是:对于任意一段子区间,最大数一定在末端,最小数一定在开头,快速找到这两个数的位置 分类分治

    如果最小值在最大值左边,那么最小值和最大值的区间一定是被划分的,如果刚好在所在大区间的两个端点,则这个区间就划分为一段

    若最小值或者最大值左边还有数,则划分为2个,对剩下的区间进行上述操作[l,最小值-1][最小值, 最大值]或者 [最小值,最大值][最大值+1,r]

    若最大值和最小值两侧还有数则划分为3个 [l,最小值-1][最小值,最大值][最大值+1,r]

    若最小值在最大值右边,则[l-最大值],[最大值+1,最小值-1],[最小值,r]

    这样时间复杂度是O(nlogn)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=0,num;char ch;
        while(ch=getchar(),!isdigit(ch)) if(ch=='-') f=1;num=ch-'0';
        while(ch=getchar(), isdigit(ch)) num=num*10+ch-'0';
        return f?-num:num;
    }
    const int N=3e5+5,K=21;
    int n,a[N],ans;
    int p[N]={-1},f1[N][K],f2[N][K],nxt[N][K];
    int getmin(int pre,int suf){return a[suf]<a[pre]?suf:pre;}
    int getmax(int pre,int suf){return a[pre]>a[suf]?pre:suf;}
    int Q1(int l,int r){int k=p[r-l+1];return getmin(f1[l][k],f1[r-(1<<k)+1][k]);}
    int Q2(int l,int r){int k=p[r-l+1];return getmax(f2[l][k],f2[r-(1<<k)+1][k]);}
    void dfs(int l,int r)//分治,找最大值和最小值
    {
        if(l>r) return;
        int minn=Q1(l,r),maxn=Q2(l,r);
        if(minn<=maxn)
            ++ans,dfs(l,minn-1),dfs(maxn+1,r);
        else
            dfs(l,maxn),dfs(maxn+1,minn-1),dfs(minn,r);
    }
    int main()
    {
        freopen("split.in","r",stdin);
        freopen("split.out","w",stdout);
        n=read();
        for(int i=1;i<=n;i++)
            p[i]=p[i>>1]+1,a[i]=read();
        for(int i=1;i<=n;i++)
            f1[i][0]=i,f2[i][0]=i,nxt[i][0]=i;
        for(int k=1;nxt[n+1][k-1]=n,k<p[n]+1;k++)
            for(int i=1;i<=n;i++)
                f1[i][k]=getmin(f1[i][k-1],f1[nxt[i][k-1]+1][k-1]),
                f2[i][k]=getmax(f2[i][k-1],f2[nxt[i][k-1]+1][k-1]),
                nxt[i][k]=nxt[nxt[i][k-1]+1][k-1];
        dfs(1,n);
        printf("%d",ans);
        fclose(stdin);fclose(stdout);
        return 0;
    }

    T3

    买一送一

    【题目描述】

    n 件物品,每件物品有一个价格。
    当你购买了一个价格为 x 的物品之后,可以选择一个价格严格小
    x 的物品,并把它免费拿走(也可以不拿)。
    现在,你要把这 n 个物品都买下来,问最少花费多少钱。
    【输入格式】
    第一行一个正整数 n,表示物品数量。
    接下来一行, n 个正整数,表示第 i 个物品的价格 a_i
    【输出格式】
    输出把 n 个物品都买下来最少的花费。
    【样例输入 1
    6
    3 4 5 3 4 5
    【样例输出 1
    14
    【样例解释 1
    买下价格为 4,5,5 的物品,并拿走 3,3,4 的物品,总共花费 14 元,
    可以证明这样是最少的。
    【样例输入 2
    5
    5 5 5 5 5
    【样例输出 2
    25
    【数据范围】
    本题共 7 个测试包
    对于所有数据, 1 ≤ n ≤ 200000 1 ≤ ≤ 10
    详细子任务附加限制及分值如下

    测试包编号 附加条件 分值
    1 n ≤ 20 25
    2 n ≤ 300 15
    3 n ≤ 5000 25
    4 = j (1 ≤ i < j ≤ n) 5
    5 j (1 ≤ i < j ≤ n) 5
    6 n≤50000, ≤3 10
    7 n ≤ 200000 15

    这是一道贪心!!!!

    但是我不会QAQ

    大佬的solution如下

    算法1
    考虑枚举哪些物品购买,剩下的贪心去选,判断是否能够全部选完。
    时间复杂度O(2^n*n) ,期望得分25

    算法2

    对于a[i]全部相同的,我们只能全部购买。
    对于a[i]全部不同的,我们显然是买最第 大的,取第 大的物品。
    时间复杂度 ,期望得分10分,结合算法1可以获得35分。

    算法3

    考虑把所有物品从大到小排序,对于价格相同的物品一起处理。假设当前处理到价格X ,价格为x的物品有a[x]个,令f[i]表示把所有价格大于x的物品都买(或者免费拿)完,现在还剩下 i个物品可以免费拿,最小的花费是多少。

    考虑枚举当前价格的物品买了多少个,剩下的都是免费选的。
    我枚举一个i,表示已经有i个可以免费拿这个物品要选,再枚举一个j表示这里我要购买j个物品,那
    么在满足i>=a[x]-j的情况下, 可以转移到 f[i-(a[x]-j)+j]

    时间复杂度O(n^2),期望得分65分,结合算法2可以获得75分

    算法4

    考虑a[i]<=3怎么做

    然鹅这题不会写

    原题来自CF

    http://codeforces.com/contest/335/problem/F

     

     总的来说这次模拟赛难度不大,应该要拿到100+60+15+

    然鹅我炸裂了!

    打表有风险


     

  • 相关阅读:
    MySQL:逻辑库与表管理
    MySQL:初识数据库
    MySQL:安装与配置
    C语言之指针
    C语言之二维数组
    C语言之冒泡排序
    C语言之数组
    C语言之函数的声明
    C语言之带有返回值的函数
    C语言之全局变量和局部变量
  • 原文地址:https://www.cnblogs.com/Shayndel/p/11835442.html
Copyright © 2011-2022 走看看