Codeforces940掉分记

it2025-03-02  22

掉分经过

难得这次时间比较好,下午17:35开始。 本来还很高兴,心想这回肯定不会犯困,没准排名能再上升一些呢,,可惜事与愿违……

上来a题,光看懂题就花了一些时间。 然后开始写,结果第一遍CE,第二遍WA…… 出师不利啊!气急败坏,暂时放弃。

然后看b题。 惊喜地发现这类似的题我做过,大概是个bfs?嗯dp也行? 结果发现\(n \leq 10^9\)...真不妙。 这时来了个有关a题的通知,于是又回去看a题。 终于找到之前代码中的问题了,过了pretest 接着c和d题做的还算顺利,1个小时时都过了pretest

剩一个小时,3道题。 e和f看完都没什么好的想法,e想用区间dp,f想用线段树,但好像都不太行…… 我又开始慌了……呜哇哇哇啊呀呀呀……

于是又回去看b题 发现居然有约1700人过了b的pretest,我猜这肯定不是道难题。 可能是个鬼畜的贪心? 嗯,想啊想啊想才终于想出来。 交了一发,TLE...又一发,WA...第三发,过了。

此时离比赛结束还有6分钟…… 好了,安心下楼吃晚饭吧。

于是,就这样,比了最颓的一场cf... 终测4道题都过了,但由于太慢了,所以排名哗哗向下掉啊…QwQ


题解

A. Points on the line

定义一个集合diameter为该集合中的 最大数-最小数 给定一个n元素的集合与 d,求最少去掉原集合中多少个数后该集合的diameter不超过d 输入的所有数据皆\(\leq100\)

想法

枚举集合中最后剩下的最大值 用这个最大值-d得到集合中可剩下的最小值 找出要去掉的数最少的即可。

代码

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int N = 105; int cnt[N],a[N]; int n,k; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); cnt[0]=0; for(int i=1;i<=n;i++) cnt[a[i]]++; for(int i=1;i<=100;i++) cnt[i]+=cnt[i-1]; if(n==1) { printf("0\n"); return 0;} int ans=n,f; for(int i=n;i>0;i--){ if(a[i]>k) ans=min(ans,n-cnt[a[i]]+cnt[a[i]-k-1]); else ans=min(ans,n-cnt[a[i]]); } printf("%d\n",ans); return 0; }

B. Our Tanya is Crying Out Loud

给定n,k,A,B 设x一开始等于n x每减1的代价为A,x每/k(前提为x是k的倍数)的代价为B 求将x变为1所需的最小代价 所有输入数据\(\leq 10^9\)

想法一

dp[i]表示从n变到i的最小代价 dp[i]=min{dp[i+1]+A,dp[i \(\dots\) k]+B} 但n太大了,时间空间都不行。

想法二

贪心。 先把x不断减1直到x为k的倍数 我们发现,若要x变成x/k,直接/k的代价比一点点减还大的话,直接减,一直减到1就行了,否则直接/k 循环这个过程

代码

贪心

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N = 1000000005; int n,k,A,B; int main() { scanf("%d%d%d%d",&n,&k,&A,&B); ll ans=0; int x=n,y; while(x>1){ if(x%k==0) { if((ll)B>=((ll)x-x/k)*A) { ans+=(ll)A*(x-1); x=1; } else { ans+=B; x=x/k; } } else { y=max(((x-1)/k)*k,1); ans+=((ll)x-y)*A; x=y; } } printf("%lld\n",ans); return 0; }

C. Phone Numbers

给定n,k及一个长度为n的字符串s 求一个长度为k的字符串t,t由出现在s中的字母组成,且t为满足条件的字典序比s大的第一个字符串。

想法

贪心。 若k>n,则直接在字符串s后面补上k-n个s中的最小字符即可 否则从后面的位往前考虑,看是否能变为一个更大的字符。若可以,便变成比它大的第一个字符,后面位都变成s中的最小字符。

代码

#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N = 100005; char s[N]; int n,k,t; int cnt[30],num[30]; int ans[N]; int main() { char ch; scanf("%d%d",&n,&k); scanf("%s",s); for(int i=0;i<n;i++) cnt[s[i]-'a'+1]=1; for(int i=1;i<=26;i++) cnt[i]+=cnt[i-1]; t=cnt[26]; for(int i=0;i<n;i++) num[cnt[s[i]-'a'+1]]=s[i]-'a'+1; if(k<=n){ for(int i=0;i<k;i++) ans[i]=cnt[s[i]-'a'+1]; for(int i=k-1;i>=0;i--){ if(ans[i]+1<=t){ ans[i]++; break; } ans[i]=1; } for(int i=0;i<k;i++){ ch=num[ans[i]]-1+'a'; cout<<ch; } } else{ for(int i=0;i<n;i++) ans[i]=cnt[s[i]-'a'+1]; for(int i=n;i<k;i++) ans[i]=1; for(int i=0;i<k;i++){ ch=num[ans[i]]-1+'a'; cout<<ch; } } return 0; }

D. Alena And The Heater

有两个长度为n的数组a[]和b[],其中b[]由01组成 b[1]=b[2]=b[3]=b[4]=0 有l与r 对于所有 5 ≤ i ≤ n 当 a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] > r 且 b[i - 1] = b[i - 2] = b[i - 3] = b[i - 4] = 1时 b[i]=0 当 a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] < l 且 b[i - 1] = b[i - 2] = b[i - 3] = b[i - 4] = 0时 b[i]=1 否则b[i]=b[i-1] 给定a[]与b[],求一组合法的l,r

想法

从左到右扫b数组 一开始令 l=-INF,r=INF 若存在“00001”的情况,则 l > max{a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] } 若存在“11110”的情况,则 r < min{a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] } 不断更新l,r即可

代码

#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N = 100005; int a[N]; char b[N]; int n; int maxn(int x){ int ret=-1000000000; for(int i=0;i<5;i++) ret=max(ret,a[x-i]); return ret; } int minn(int x){ return min(a[x],min(a[x-1],min(a[x-2],min(a[x-3],a[x-4])))); } int main( { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); scanf("%s",b); int l=-1000000000,r=1000000000,f=0; for(int i=0;i<n;i++){ if(f<4) f++; else{ if(b[i]=='1' && b[i-1]=='0'){ l=max(l,maxn(i)+1); f=1; } else if(b[i]=='0' && b[i-1]=='1'){ r=min(r,minn(i)-1); f=1; } } } printf("%d %d\n",l,r); return 0; }

E. Cashback

给定一个n元素的数组a[],给定一个数c 规定对于一个k元素的数组b[],其价值为b[]中所有元素的和 - 其中前\(\lfloor \frac{k}{c} \rfloor\)小的元素的和 将a数组分为若干个连续的子数组,求这些子数组的价值和的最小值\(n \leq 10^5\)

想法

贪心。 价值和最小,即去掉的前\(\lfloor \frac{k}{c} \rfloor\)小的元素的和最大 我们可以发现,若有一个长度为2c的子数组,那么考虑这个子数组前2小的两个元素i与j 若i与j同在这个子数组的前一半或后一半,那么将这个子数组分为两个长度为c的子子数组价值和会更小 若它们一个在前一半,一个在后一半,那么可以直接将这个子数组分为前后两部分。 故将a数组划分成的子数组长度为1或c dp+单调队列优化即可

代码

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N = 100005; int a[N]; ll sum[N],f[N]; int que[N],head,tail; int n,c; int main() { scanf("%d%d",&n,&c); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++){ f[i]=f[i-1]+a[i]; while(head<tail && que[head]<=i-c) head++; while(head<tail && a[que[tail-1]]>=a[i]) tail--; que[tail++]=i; if(i>=c) f[i]=min(f[i],f[i-c]+sum[i]-sum[i-c]-a[que[head]]); } printf("%lld\n",f[n]); return 0; }

F. Machine Learning

给定一个数组a[],维护下面两种操作: 1.求一段区间[l,r]的mex 2.将a[p]的值改为x 假设在某区间中每个数出现的次数为cnt[i] 这个区间mex为cnt[]中不存在的最小的正整数

想法

当时比赛的时候还不知道莫队算法 后来才发现原来这就是个带修改莫队的模板题

代码

#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<map> using namespace std; const int N = 200005; int read(){ char ch=getchar(); int x=0; if(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x; } int block; inline int bl(int x) { return (x-1)/block+1; } struct que{ int l,r,tim,id; bool operator < (const que &b) const{ return bl(l)<bl(b.l) || (bl(l)==bl(b.l) && bl(r)<bl(b.r)) || (bl(l)==bl(b.l) && bl(r)==bl(b.r) && tim<b.tim); } }q[N]; struct cg{ int id,fr,to; }d[N]; int n,m,cnt1,cnt2; int a[N]; map<int,int> num; int nn; int size[N],cnt[N],L,R; void add(int x){ size[cnt[x]]--; size[++cnt[x]]++; } void del(int x){ size[cnt[x]]--; size[--cnt[x]]++; } void cg_add(cg w){ if(L<=w.id && w.id<=R){ del(w.fr); add(w.to); } a[w.id]=w.to; } void cg_del(cg w){ if(L<=w.id && w.id<=R){ del(w.to); add(w.fr); } a[w.id]=w.fr; } int ans[N]; inline int query(){ for(int i=1;i<=n;i++) if(size[i]==0) return i; } int main() { int opt,x,y; n=read(); m=read(); for(int i=1;i<=n;i++) { a[i]=read(); if(!num[a[i]]) num[a[i]]=++nn; a[i]=num[a[i]]; } for(int i=1;i<=m;i++){ opt=read(); x=read(); y=read(); if(opt==1) q[++cnt1]=(que){x,y,cnt2,cnt1}; else{ if(!num[y]) num[y]=++nn; y=num[y]; d[++cnt2]=(cg){x,a[x],y}; a[x]=y; } } block=pow(n,0.666); sort(q+1,q+1+cnt1); L=1; R=0; int t=cnt2; for(int i=1;i<=cnt1;i++){ while(R<q[i].r) add(a[++R]); while(L>q[i].l) add(a[--L]); while(R>q[i].r) del(a[R--]); while(L<q[i].l) del(a[L++]); while(t>q[i].tim) cg_del(d[t--]); while(t<q[i].tim) cg_add(d[++t]); ans[q[i].id]=query(); } for(int i=1;i<=cnt1;i++) printf("%d\n",ans[i]); return 0; }

终于……

转载于:https://www.cnblogs.com/lindalee/p/8471362.html

相关资源:数据结构—成绩单生成器
最新回复(0)