AtCoder Beginner Contest 367 - VP记录
Preface
被 D 题搞得有点崩(我怎么老是跟黄题过不去,绿题都不敢这么卡我),没发挥好,下次记得题读清楚,思路理顺了再写核心代码。
A - Shout Everyday
AtCoder 比赛高桥出镜率 \(100\%\)。
为什么洛谷月赛没有 kkk 出镜呢?
点击查看代码
#include<cstdio> using namespace std; int main() { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(b>c) c+=24; for(int i=b;i<c;i++) if(a==i%24) { printf("No\n"); return 0; } printf("Yes\n"); return 0; }
B - Cut .0
printf
小妙招(正式考试我可不敢这么玩)
点击查看代码
#include<cstdio> using namespace std; int main() { double x; scanf("%lf",&x); if(x<10) printf("%.4g",x); else printf("%.5g",x); return 0; }
C - Enumerate Sequences
数据太小,直接 DFS 暴搜,最后统一排序。
时间复杂度 \(O(R^N \times N)\)。
点击查看代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=10,ReN=4e5+5; int n,k,r[N]; int a[N]; struct Answer{ int num[N]; }ans[ReN]; int ans_idx; void DFS(int pos) { if(pos>n) { int sum=0; for(int i=1;i<=n;i++) sum+=a[i]; if(sum%k==0) { ans_idx++; memcpy(ans[ans_idx].num,a,sizeof(a)); } return; } for(int i=1;i<=r[pos];i++) { a[pos]=i; DFS(pos+1); } return; } bool cmp(Answer x,Answer y) { for(int i=1;i<=n;i++) if(x.num[i]!=y.num[i]) return x.num[i]<y.num[i]; return false; } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&r[i]); DFS(1); sort(ans+1,ans+ans_idx+1,cmp); for(int i=1;i<=ans_idx;i++) { for(int j=1;j<=n;j++) printf("%d ",ans[i].num[j]); putchar('\n'); } return 0; }
D - Pedometer
出现了,神之黄题。
一眼看出做法,赛事一直调不对,整个人都不好了。
首先环转链,把数组复制一份接在末尾。
然后记录模 \(m\) 后的前缀和,即 \(sum_i = \sum_{j=1}^{i} a_j \bmod m\)。
如果 \(sum_{l-1} = sum_r\),那么 \(a_{l \sim r}\) 就是一段合法序列。
所以每次累加这个数前面和它相等的数的个数就行了……吗?
首先,\(l\) 和 \(r\) 的距离不能超过 \(n\);
其次,\(a_i\) 表示的是 \(i\) 到 \(i+1\) 的距离,所以 \(a_n\) 与众不同;
最后,复制的那一部分内部不准互相组合,只准和前一半组合。
综上所述,遍历的时候需要分 \([1,n-1]\) 和 \([n,2n-1]\) 两部分(什么诡异分法),前面一半需要不停加,后面一半需要不停减。
真的给我恶心到了。
时间复杂度 \(O(N)\)。
#include<cstdio> #include<cstring> using namespace std; const int N=2e5+5,M=1e6+5; int n,m,a[N<<1]; int sum[N<<1],cnt[M]; long long ans; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[n+i]=a[i]; } for(int i=1;i<n<<1;i++) sum[i]=(sum[i-1]+a[i])%m; cnt[sum[0]]++; for(int i=1;i<n;i++) { ans+=cnt[sum[i]]; cnt[sum[i]]++; } for(int i=n;i<n<<1;i++) { cnt[sum[i-n]]--; ans+=cnt[sum[i]]; } printf("%lld\n",ans); return 0; }
E - Permute K times
我的快速幂白学了。
原来只要满足结合律就可以快速幂啊。
因为先走 \(x\) 步再走 \(y\) 步和一次性走 \((x+y)\) 步是一样的,所以本题中的操作过程满足结合律。
因此,就可以将走 \(k\) 步分成走两次 \(\lfloor \frac{k}{2} \rfloor\),\(k\) 是奇数时另外单独操作一步。
然后像快速幂一样拆分合并就可以了,你甚至可以直接套快速幂板子,只需要改一下乘法就行了。
#include<cstdio> #include<array> using namespace std; const int N=2e5+5; int n; long long k; array<int,N> x,a; array<int,N> arr_merge(array<int,N> &x,array<int,N> &y) { static array<int,N> res; for(int i=1;i<=n;i++) res[i]=x[y[i]]; return res; } int main() { scanf("%d%lld",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&x[i]); for(int i=1;i<=n;i++) scanf("%d",&a[i]); while(k) { if(k&1) a=arr_merge(a,x); x=arr_merge(x,x); k>>=1; } for(int i=1;i<=n;i++) printf("%d ",a[i]); return 0; }
F - Rearrange Query
学到了一个 Trick:随机化哈希。
给每个数赋一个随机数权值,然后直接计算区间和是否相等。
真够暴力的,时间复杂度 \(O(N+Q)\)。
(注意这种题千万不要用异或,这样出题人乱造数据都可以卡掉,因为两相等数异或得 \(0\),也即是说两个 \(x\) 和四个 \(x\) 在这里是一样的,数据一旦出现两范围内有多个相同数,那就大概率丸辣。)
#include<cstdio> #include<ctime> #include<random> using namespace std; const int N=2e5+5; int n,q,a[N],b[N]; unsigned long long suma[N],sumb[N]; unsigned int rand_map[N]; int main() { mt19937 engine((unsigned)time(nullptr)); scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(!rand_map[a[i]]) rand_map[a[i]]=engine(); suma[i]=suma[i-1]+rand_map[a[i]]; } for(int i=1;i<=n;i++) { scanf("%d",&b[i]); if(!rand_map[b[i]]) rand_map[b[i]]=engine(); sumb[i]=sumb[i-1]+rand_map[b[i]]; } for(int i=1;i<=q;i++) { int l1,r1,l2,r2; scanf("%d%d%d%d",&l1,&r1,&l2,&r2); printf("%s\n",suma[r1]-suma[l1-1]==sumb[r2]-sumb[l2-1] ? "Yes":"No"); } return 0; }
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18511410
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步