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的表,表炸了
事实告诉我们
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+
然鹅我炸裂了!
打表有风险