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+

然鹅我炸裂了!

打表有风险


 

原文地址:https://www.cnblogs.com/Shayndel/p/11835442.html