隐藏页面特效

Codeforces Round 887 (Div. 1)

1|0Preface


补一篇好久之前打的CF的博客,说实话其实B题我当时怎么想的已经忘的七七八八了

这场第一眼看A没啥思路,就先去把B这个构造写了,中间想了挺久的大概40min才写出来

然后回头一看A发现可以倒着做,就是个丁真题了然后15min写完

后面看C刚开始以为是个贪心,后面又感觉是个DP,但状态数比我预计的多,搞来搞去也没写出来

后面看Tutorial才发现原来是带悔贪心,捏麻麻的好像自从ZJOI2020那道题之后就再也没见到过这个算法的题了,苦路西


2|0A. Ntarsis' Set


正着想不太好搞(其实也能做),安利一手徐神的正序的O(n)做法——CF1852A 中的结论证明

其实不妨倒着考虑问题,我们不妨将进行了i次删数操作的集合称为版本i+1,则我们现在要求的是版本k+1中第一小的元素

那么倒着想这个元素其实就是版本k中第一个没有出现的元素,我们记此时它的位次为rk

那么再往前推一步这个元素其实就是版本k1中第rk个没有出现的元素,不难发现可以一路推回到版本1中第几个没有出现的元素

具体实现可以直接用一个指针来单调移动,但比赛的时候我降智了写了个二分,不过无伤大雅

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long u64; typedef pair <int,int> pi; const int N=200005; int t,n,k,a[N],cnt,num[N],l[N],r[N],pfx[N]; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%lld",&t);t;--t) { RI i; for (scanf("%lld%lld",&n,&k),cnt=0,i=1;i<=n;++i) if (scanf("%lld",&a[i]),a[i]>a[i-1]+1) num[++cnt]=a[i]-a[i-1]-1,l[cnt]=a[i-1]+1,r[cnt]=a[i]-1; for (i=1;i<=cnt;++i) pfx[i]=pfx[i-1]+num[i]; int ret=1; for (i=1;i<=k;++i) { int pos=lower_bound(pfx+1,pfx+cnt+1,ret)-pfx; if (pos>cnt) { ret=a[n]+ret-pfx[cnt]; continue; } ret=l[pos]+(ret-pfx[pos-1]-1); } printf("%lld\n",ret); } return 0; }

3|0B. Imbalanced Arrays


原谅我这题真忘了我当时怎么想的了,就说一个大概的印象吧

首先我们将ai从大到小排序,同时我们设数组bi表示对于每一个ai,我们都对b1bai进行区间加1操作

则若i[1,n],aibi则一定无解,不难发现这是充要条件

否则我们可以找到一个分界点pos[1,n],满足[1,pos]中所有数最后取值都是正的,而[pos+1,n]中所有数最后取值都是负的

由于最后数的值域是[n,n],我们不妨从n1枚举每个数,然后考虑每次要么将正的这个数放在前面部分,要么将负的这个数放在后面部分

具体放在哪边的话可以把贡献的式子写出来和目标的式子对比一下,不难发现这样一定可以构造出一组合法的解

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long u64; typedef pair <int,int> pi; const int N=200005; int T,n,a[N],b[N],ans[N]; pi t[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&T);T;--T) { RI i,j,k; for (scanf("%d",&n),i=0;i<=n;++i) b[i]=0; for (i=1;i<=n;++i) scanf("%d",&a[i]),++b[1],--b[a[i]+1],t[i]=pi(a[i],i); for (i=1;i<=n;++i) b[i]+=b[i-1]; bool flag=1; for (sort(t+1,t+n+1,greater<pi>()),i=1;i<=n&&flag;++i) if (t[i].fi!=b[i]) flag=0; if (!flag) { puts("NO"); continue; } int pos=n; for (i=1;i<=n;++i) if (b[i]<=i-1) { pos=i; break; } for (puts("YES"),i=1,j=n,k=n;k>=1;--k) if (i+(k-1)==b[i]) ans[t[i++].se]=k; else ans[t[j--].se]=-k; for (i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]); } return 0; }

4|0C. Ina of the Mountain


首先考虑以下经典问题,给定数列{an},每次可以选择将一个区间内的所有数减去1,求最后最少的操作次数使得所有数为0

根据差分我们容易知道最后的答案其实就是i=1nmax(0,aiai1),但这道题相当于是可以将每个数加上若干个k,求上述式子的最小值

考虑以下贪心的想法,首先不妨设aiai1加上的k的数量相同,则此时:

  • ai<ai1,则上述式子的贡献为0,贪心地认为不动是最优的
  • aiai1,则有两种情况:
    • 不做其它修改,此时对答案的贡献为aiai1
    • t[j,i)的所有at都加上k,不难发现此时对答案的贡献为ajaj1+k

由于我们每次要求出当前的最优代价,因此考虑将对[j,i)操作的这种方案视为反悔操作,用一个小根堆来维护所有反悔操作的最小贡献即可

要注意的是若我们选择在i这个时候反悔选择对[j,i)进行操作,那么后续反悔选择在i这个位置时要额外付出的代价其实是aiai1而非aiai1+k,因为ai1的值已经比原来的大k

剩下的细节看代码吧,复杂度O(nlogn)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<limits.h> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; const int N=200005; int t,n,k,a[N]; LL ans; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { priority_queue <int,vector <int>,greater <int>> hp; RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]); for (ans=0,i=1;i<=n;++i) { int now=a[i]%k,pre=a[i-1]%k; if (now<pre) hp.push(now+k-pre); else hp.push(now-pre),ans+=hp.top(),hp.pop(); } printf("%lld\n",ans); } return 0; }

5|0Postscript


如果能像这样一点一点地蹭上2100就好了……


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17601810.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(127)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2022-08-02 CodeTON Round 2
2018-08-02 CF [2016-2017 ACM-ICPC CHINA-Final][GYM 101194 H] Great Cells
2018-08-02 数列分块入门九题(三):LOJ6283~6285
2018-08-02 NOIp膜你赛三 简要题解
2018-08-02 NOIp膜你赛二 简要题解
2018-08-02 NOIp膜你赛一 简要题解
点击右上角即可分享
微信分享提示