隐藏页面特效

Educational Codeforces Round 148 (Rated for Div. 2)

1|0Preface


补题,这场比较简单,E之前的都能写出来,当然D的细节挺多的WA了好几发

感觉时间好不够用啊,想补的题那么多但效率好低,可能要等暑假才能集中攻克了


2|0A. New Palindrome


统计下出现了一次以上的字符有几种,如果大于等于两种就有解

#include<cstdio> #include<iostream> #include<cstring> #define RI register int #define CI const int& using namespace std; const int N=55; int t,n,c[N]; char s[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%s",s+1),n=strlen(s+1),i=0;i<26;++i) c[i]=0; for (i=1;i<=n;++i) ++c[s[i]-'a']; int cur=0; for (i=0;i<26;++i) if (c[i]>1) ++cur; puts(cur>1?"YES":"NO"); } return 0; }

3|0B. Maximum Sum


直接枚举最后取了多少次最大值,然后取最小值的次数也是知道的,贡献用前缀和算一下就行

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=200005; int t,n,k,a[N]; long long pfx[N],ans; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]); for (sort(a+1,a+n+1),pfx[0]=0,i=1;i<=n;++i) pfx[i]=pfx[i-1]+a[i]; for (ans=i=0;i<=k;++i) ans=max(ans,pfx[n-i]-pfx[2*(k-i)]); printf("%lld\n",ans); } return 0; }

4|0C. Contrast Value


不难发现如果原序列中存在三个相邻的数ai,ai+1,ai+2之间有aiai+1ai+2aiai+1ai+2,则中间那个就可以被删去

具体实现的话可以记录一下相邻两数之间的大小关系,如果相等就直接忽略,否则和上一次的对比下即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=300005; int t,n,a[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]); int lst=-2,ret=1; for (i=1;i<n;++i) { int cur=a[i]==a[i+1]?0:(a[i]<a[i+1]?-1:1); if (cur) ret+=cur!=lst,lst=cur; } printf("%d\n",ret); } return 0; }

5|0D1. Red-Blue Operations (Easy Version)


首先要求最小值最大一眼二分答案x,然后考虑如何check

首先很容易发现一个数被操作偶数次后值一定减小,操作奇数次一定增大

同时增大的最大情况就是只操作一次,比如只对某个位置进行一次+k操作是理论上能得到的最大值,否则由于排除掉最大的这次操作后剩下的操作为偶数,一定会让增量减小

因此就有了一个贪心的思路,给所有数从小到大排序后,给所有小于x的数先只加上最大的可能增量

然后考虑剩下的操作如何安排,不难发现把它们两个一组打包作减法是影响最小的,看下剩下的余量能否满足要求即可

但是还有各种边界情况,比如当初始时大于等于x的数有两个及以上时是一定合法的(因为偶数次操作可以被拆分为若干次奇数操作,增量一定是正的)

然后就很容易写出一个O(n)check的代码,足以通过本题(后面D2就是直接在D1的代码上优化的,因此一定要理解这里的思路)

#include<cstdio> #include<iostream> #include<algorithm> #define int long long #define RI register int #define CI const int& using namespace std; const int N=1005; int n,q,k,a[N],b[N],tp[N]; inline bool check(CI x) { RI i; int cur=k; for (i=1;i<=n;++i) b[i]=a[i],tp[i]=0; for (i=1;i<=n&&cur>=1;++i) if (b[i]<x) b[i]+=cur--,tp[i]=1; for (i=1;i<=n;++i) if (b[i]<x) return 0; if (cur<1) return 1; int pos=0,cnt=0; for (i=1;i<=n;++i) if (!tp[i]) ++cnt,pos=i; if (cnt>=2) return 1; if (cur%2==1&&pos) b[pos]+=cur--; if (cur%2==1) return 0; for (i=1;i<=n&&cur>=1;++i) if (b[i]>x) cur-=2*(b[i]-x),b[i]=x; if (cur<1) return 1; return 0; } signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%lld%lld",&n,&q),i=1;i<=n;++i) scanf("%lld",&a[i]); for (sort(a+1,a+n+1),i=1;i<=q;++i) { scanf("%lld",&k); int l=-2e9,r=2e9,mid,ret; while (l<=r) if (check(mid=l+r>>1)) ret=mid,l=mid+1; else r=mid-1; printf("%lld ",ret); } return 0; }

6|0D2. Red-Blue Operations (Hard Version)


有了上面的代码其实优化就非常显然了,我们发现找小于x的位置可以二分,后面算贡献可以拆分一下式子直接O(1)

唯一需要注意的是就是由于可能有相邻的数,因此在第一步检验每个数是否能做一次操作来大于等于x的过程中要找一个aii最小的位置,这个可以预处理一下

然后就没啥了,总复杂度就是O(qlogailogn)

#include<cstdio> #include<iostream> #include<algorithm> #define int long long #define RI register int #define CI const int& using namespace std; const int N=200005; int n,q,k,a[N],mipos[N],all; inline bool check(CI x) { int pos=lower_bound(a+1,a+n+1,x)-a-1,cur=k,left=0; if (pos>0&&a[mipos[pos]]+k-mipos[pos]+1<x) return 0; cur-=pos; if (cur<0) return 0; if (n-pos>=2) return 1; if (cur%2==1&&n-pos>=1) left+=cur--; if (cur%2==1) return 0; left+=all+(2*k-pos+1)*pos/2LL-n*x; cur-=2*left; if (cur<=0) return 1; return 0; } signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%lld%lld",&n,&q),i=1;i<=n;++i) scanf("%lld",&a[i]),all+=a[i]; for (sort(a+1,a+n+1),mipos[1]=1,i=2;i<=n;++i) if (a[i]-i<a[mipos[i-1]]-mipos[i-1]) mipos[i]=i; else mipos[i]=mipos[i-1]; for (i=1;i<=q;++i) { scanf("%lld",&k); int l=-2e9,r=2e9,mid,ret; while (l<=r) if (check(mid=l+r>>1)) ret=mid,l=mid+1; else r=mid-1; printf("%lld ",ret); } return 0; }

7|0E. Combinatorics Problem


怎么会有这么简单的数学题的说,我感觉就是一个Div2C题的难度顶天了

处理组合数的贡献的经典方法就是差分(或者说根据组合数的递推公式),由于Cx+1kCxk=Cxk1,因此我们发现当i增加时增量可以用之前的某些东西来表示

具体的,设fi,j表示当k=jbi的值,稍微手玩一下很容易得到转移:

fi,j=fi1,j+fi1,j1+[j1]ai

后面的那个[j1]其实就是C1j,因为j2时不能直接把ai加入bi的贡献中

总复杂度O(nk)

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=1e7+5,mod=998244353; int n,a[N],x,y,m,k,f[N][6]; long long ans; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j; scanf("%d%d%d%d%d%d",&n,&a[1],&x,&y,&m,&k); for (i=2;i<=n;++i) a[i]=(1LL*a[i-1]*x+y)%m; for (i=1;i<=n;++i) for (j=0;j<=k;++j) f[i][j]=(1LL*(j?f[i-1][j-1]:0)+f[i-1][j]+(j<=1?a[i]:0))%mod; for (i=1;i<=n;++i) ans^=1LL*f[i][k]*i; return printf("%lld",ans),0; }

8|0Postscript


感觉好久没现场打CF的比赛了,还是要跟进一下不然手生了就很难受


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17425534.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2019-05-23 CF 1119F Niyaz and Small Degrees
2018-05-23 【LGR-047】洛谷5月月赛
点击右上角即可分享
微信分享提示