隐藏页面特效

AtCoder Regular Contest 174

1|0Preface


这场当时在和同学开黑打LOL都不知道有这回事,后面看徐神和祁神在讨论才知道有ARC

赛后补了下发现中规中矩,D题在已知是个打表题的前提下很好做,但如果真在比赛的时候是否能用有限的时间找到规律也是存疑


2|0A - A Multiply


找出原序列的最大/最小子段和,讨论下操作哪个得到的结果最优即可

#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<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=300005; int n,c,a[N],sum,mx,mn,cur1,cur2; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%lld%lld",&n,&c),i=1;i<=n;++i) scanf("%lld",&a[i]),sum+=a[i]; for (RI i=1;i<=n;++i) { cur1=max(0LL,cur1+a[i]); mx=max(mx,cur1); cur2=min(0LL,cur2+a[i]); mn=min(mn,cur2); } return printf("%lld",max(sum+(c-1)*mx,sum+(c-1)*mn)),0; }

3|0B - Bought Review


将题意稍做转化,令\(S=2\times A_1+A_2-A_4-2\times A_5\),我们现在的目标就是要将\(S\)变为\(\le 0\)

操作为花费\(P_4\)的代价使\(S\)\(1\);或者花费\(P_5\)的代价使\(S\)\(2\)

不难发现最优方案要么全部用其中一种操作,要么用一次减\(1\)用若干次减\(2\)(当\(S\)为奇数时)

#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<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=300005; int t,a[10],p[10]; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%lld",&t);t;--t) { RI i; int cnt=0,sum=0; for (i=1;i<=5;++i) scanf("%lld",&a[i]); for (i=1;i<=5;++i) scanf("%lld",&p[i]); int tmp=a[1]*2+a[2]-a[4]-a[5]*2; if (tmp<=0) { puts("0"); continue; } printf("%lld\n",min({tmp*p[4],(tmp+1)/2*p[5],tmp/2*p[5]+(tmp%2)*p[4]})); } return 0; }

4|0C - Catastrophic Roulette


经典ARC数数题,但这个还行闪总都会做

注意到\(n\)的范围可以枚举,因此一眼考虑DP,设\(f_{i,0/1}\)表示第一次出现\(i\)个数时,当前操作者是先手/后手的概率

对于状态\(f_{i,0}\)\(f_{i,1}\)同理),直接来看它有\(P=\frac{i}{n}\)的概率转移到\(f_{i,1}\),并对先手产生\(1\)的花费;同时有\(1-P\)的概率转移到\(f_{i+1,1}\)

注意到这个DP转移的时候会在\(i\)相同的两个状态间循环转移,乍一看感觉不好处理,但仔细一想会发现贡献其实都是等比数列形式

\(f_{i,0}\)的转移为例,在经过了无限次操作后显然它必然会到达\(f_{i+1,0/1}\)中的一个,手玩一下概率会发现有:

  • \(f_{i+1,1}\leftarrow f_{i,0}\times \frac{1}{1+P}\),同时对于先手的花费期望贡献为\(f_{i,0}\times \frac{P}{1-P^2}\)
  • \(f_{i+1,0}\leftarrow f_{i,0}\times \frac{P}{1+P}\),同时对于后手的花费期望贡献为\(f_{i,0}\times \frac{P^2}{1-P^2}\)

对于这类状态可能重复多次的问题,不妨稍微修改定义,只统计第一次遇到某个状态时的概率/期望,问题就迎刃而解了

#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<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=1e6+5,mod=998244353; int n,f[N][2],ans[2]; inline int quick_pow(int x,int p=mod-2,int mul=1) { for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j; scanf("%d",&n); int inv_n=quick_pow(n); for (f[1][1]=i=1;i<n;++i) for (j=0;j<2;++j) { int p=1LL*i*inv_n%mod; (ans[j]+=1LL*f[i][j]*p%mod*quick_pow((1LL-1LL*p*p%mod+mod)%mod)%mod)%=mod; (ans[j^1]+=1LL*f[i][j]*p%mod*p%mod*quick_pow((1LL-1LL*p*p%mod+mod)%mod)%mod)%=mod; (f[i+1][j^1]+=1LL*f[i][j]*quick_pow((1+p)%mod)%mod)%=mod; (f[i+1][j]+=1LL*f[i][j]*p%mod*quick_pow((1+p)%mod)%mod)%=mod; } return printf("%d %d",ans[0],ans[1]),0; }

5|0D - Digit vs Square Root


看到题目直觉告诉我们符合条件的\(x\)感觉不会很多,手玩一下会发现确实如此

因此考虑打表来找合法的\(x\)是否存在某些规律,不过直接暴枚\(x\)的话复杂度会有点炸(虽然感觉来了一样能看出规律),故考虑枚举\(y\)来判断其合法的区间

首先\(x\in[y^2,(y+1)^2-1]\)是trivial的,考虑要满足\(y\)\(x\)的前缀的性质,这个只要找出\(\le y^2\)且以\(y\)为前缀的最小数以及\(\le (y+1)^2-1\)且以\(y\)为前缀的最大数即可

前者很好处理,我们在\(y\)后面补\(0\)直到其值\(\ge y^2\)即可,后者的话需要一点小讨论,具体可以看打表代码

#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<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=1e6; signed main() { freopen("BF.txt","w",stdout); printf("{%lld,%lld},",1LL,1LL); for (RI y=4;y<=N;++y) { int l=y*y,r=(y+1)*(y+1)-1; int tl=y,tr=r; while (tl<l) tl*=10; auto calc_len=[&](int x) { int len=0; while (x>0) x/=10,++len; return len; }; int len_y=calc_len(y),len_r=calc_len(r); for (RI i=1;i<=len_r-len_y;++i) tr/=10; if (tr>y) { tr=y; for (RI i=1;i<=len_r-len_y;++i) tr=tr*10+9; } else if (tr<y) { tr=y; for (RI i=1;i<len_r-len_y;++i) tr=tr*10+9; } else tr=r; if (tl<=tr) printf("{%lld,%lld},",tl,tr); } return 0; }

把表打出来后就会发现显而易见的规律,即除了\([1,1]\)合法的区间就两种形式:

  • 单个数:末尾\(i\)\(0\),开头是\(i-1\)\(9\)再接上一个\(8\),如\(\{998000\},\{99980000\}\)
  • 一段区间:左端点为\(i\)\(9\)接上\(i\)\(0\);右端点为\(10^{2i}+10^{i}-1\),如\([999000,1000999],[99990000,100009999]\)

不难发现合法的区间个数很少,预先处理出来后询问的时候直接枚举每个区间即可

#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<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; int t,n,pw[20]; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j; for (pw[0]=i=1;i<=18;++i) pw[i]=pw[i-1]*10; vector <pi> table={{1,1}}; for (i=1;i<=9;++i) { int x=0; for (j=1;j<i;++j) x=x*10+9; x=x*10+8; for (j=1;j<=i;++j) x*=10; table.push_back({x,x}); x=0; for (j=1;j<=i;++j) x=x*10+9; for (j=1;j<=i;++j) x*=10; if (i==9) table.push_back({x,pw[18]}); else table.push_back({x,pw[i]+pw[2*i]-1}); } for (scanf("%lld",&t);t;--t) { scanf("%lld",&n); int ans=0; for (auto [l,r]:table) if (n>=l) ans+=min(n,r)-l+1; else break; printf("%lld\n",ans); } return 0; }

6|0Postscript


后面E题虽然过的人挺多,但看到Counting一眼寄,还是去准备准备DS专题的搬题吧


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18083264.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(123)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
历史上的今天:
2023-03-19 Codeforces Round 858 (Div. 2)
点击右上角即可分享
微信分享提示