[题解]ABC347 C~E

Portal:https://atcoder.jp/contests/abc347/tasks

ABC347只过了\(A,B\),再创新低,。。。遂来补题

C - Ideal Holidays

题意简述

输入\(n,a,b,d_1,d_2,…,d_n\),表示在Atcoder国每周分为\(a\)天休息日和\(b\)天工作日,现在有\(n\)个事件,第\(i\)个事件落在第\(d_i\)日。我忘了今天是这一周的第几天,需要你输出这\(n\)个事件是否有可能全部落在休息日。

分析

一开始是对\(n\)个日期取模\(a+b\)然后再求极差\(k\),如果\(k<a\)Yes,否则No……

然后就\(WA\)了一个点。见下:

2 5 1
5 6

\(2\)个事件:\(\{5,6\}\)\(5\)天休息日,\(1\)天工作日。本应输出Yes,但是输出了No。这是因为对\(5\)\(6\)取模\(6\)分别得\(5\)\(0\),所以只判断极差是不行的。应该把所有\(d[i]\)取模后的结果存在\(d\)中,从小到大排序,然后把两个日期之间的天数(包括\(d[1]+a+b-d[n]\))求最大值。因为\(d[i]\)取值范围是\([0,a+b)\),所以如果两个天数之间差距\(\geq b\)(工作日的天数),就说明这些日期都能安排在休息日。

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,a,b,d[200010];
int main(){
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
cin>>d[i];
d[i]%=(a+b);
}
sort(d+1,d+1+n);
int maxx=INT_MIN;
for(int i=2;i<=n;i++){
maxx=max(maxx,d[i]-d[i-1]-1);
}
maxx=max(maxx,d[1]+a+b-d[n]-1);
if(maxx>=b) cout<<"Yes\n";
else cout<<"No\n";
}

D - Popcount and XOR

题意简述

给你非负整数\(a,b,C\),构造出非负整数\(X,Y\)满足下列条件:

  • \(0 \leq X < 2^{60}\)
  • \(0 \leq Y < 2^{60}\)
  • \(\operatorname{popcount}(X) = a\)
  • \(\operatorname{popcount}(Y) = b\)
  • \(X \oplus Y = C\)

\(popcount(X)\)表示\(X\)二进制表示中\(1\)的个数。
\(\oplus\)表示按位异或。

分析

这个题难度其实并不高,但因为赛时跟C鏖战太久并且吃了\(4\)次罚时还没\(A\),所以思路乱了。第二天一想就出来了。

\(k\)\(C\)\(1\)的个数,\(x,y\)分别为\(X,Y\)中,放在\(C\)值为\(1\)的位置上的\(1\)的个数。则有下列关于\(x,y\)的方程组:

\(\begin{cases} a-x=b-y\\ x+y=k \end{cases}\)

则有\(\begin{cases} x=\frac{a-b+k}{2}\\ y=k-x \end{cases}\)

那么我们总结了部分输出No的情况:

  • \((a-b+k)\ mod\ 2\neq 0\)(注意负数\(mod\ 2\)可能等于\(-1\));
  • \(x<0,y<0,x\geq a或y\geq b\)

根据目前的结果,我们可以进行构造了,\(0\leq i<60\)\(cnt0\)\(cnt1\)分别表示到目前\(s[i]=0和1\)的个数。详见代码。

需要注意的是还有一种输出No的情况,也就是构造完,\(a\)\(1\)或者\(b\)\(1\)没有用完。比如\(a=60,b=60,c=2^{60}-1\),不会被前面的步骤筛查出来,但是发现\(X\)\(Y\)中一共只能用\(60\)\(1\)\(a\)\(b\)没有用完。所以需要定义\(cnt\)来记录填了多少\(1\),每构造完一个就判断如果\(cnt<a\)(如果构造的是\(Y\)就判断\(cnt<b\))就输出No

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c;
signed main(){
cin>>a>>b>>c;
bitset<70> s(c),A,B;
int k=s.count();
int dx=a-b+k;
if(dx%2!=0){
cout<<"-1\n";
return 0;
}
int x=dx/2,y=k-x;
if(x<0||y<0||x>a||y>b){
cout<<"-1\n";
return 0;
}
int cnt=0;
for(int i=0,cnt0=0,cnt1=0;i<60;i++){
if(s[i]==0){
if(cnt0<a-x) A[i]=1,cnt++;
cnt0++;
}else{
if(cnt1<x) A[i]=1,cnt++;
cnt1++;
}
}
if(cnt<a){
cout<<"-1\n";
return 0;
}
cnt=0;
for(int i=0,cnt0=0,cnt1=0;i<60;i++){
if(s[i]==0){
if(cnt0<b-y) B[i]=1,cnt++;
cnt0++;
}else{
if(cnt1>=x) B[i]=1,cnt++;
cnt1++;
}
}
if(cnt<b){
cout<<"-1\n";
return 0;
}
cout<<A.to_ullong()<<" "<<B.to_ullong()<<"\n";
return 0;
}

E - Set Add Query

题意简述

给定\(n,q\),接下来给出\(q\)个数\(x[1],x[2],…,x[q]\)

定义\(A\)数组初始全为\(0\),集合\(S\)为空,对于\(i=1,2,…,n\),依次进行下列操作:

  • 如果\(x[i]\in S\),则把\(x[i]\)\(S\)中删除。
  • 如果\(x[i]\not\in S\),则把\(x[i]\)加入\(S\)
  • 接下来,对于\(j=1,2,…n\),如果\(j\in S\),则\(A[j]+=|S|\)
    \(|S|\)表示集合\(S\)的大小)

更详细的解释见原题样例。

分析

赛后看的这道题,感觉比C和D简单。

拿下面的样例举例:

5 7
1 2 3 2 5 4 2

如上图,下面的彩色条表示的是某一个数字在哪些步骤时待在\(S\)里。例如\(1\)全程都在\(S\)中,\(2\)中间断了一下,是因为又出现了一个\(2\),所以\(2\)\(S\)中被删除了。那么我们可以用\(q\)次循环完成这个过程,把每一个区间用\((l,r)\)的形式存在pair数组中。具体见代码。

处理pair数组的同时,我们应该把每次操作后\(S\)的大小存下来。用差分+前缀和即可维护出来,存在\(sum\)数组中。接下来输出\(n\)个值,我们就把\(sum\)\(i\)对应的区间求和输出即可。但是如果循环求和会超时,所以我们需要再求一次前缀和(总体步骤就是差分+前缀和+前缀和)。

https://atcoder.jp/contests/abc347/submissions/51969480的思路与这个相似,但是代码更精简,思路就是用\(k\)表示从\(1\)\(i\)的长度之和,也是前缀和的方法,但是先减后增,遇到第奇数个数字\(num\)就减去当前的\(k\),遇到第偶数个\(num\)就加上当前的\(k\)

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,x[200010],last[200010],cnt;
bitset<200010> vis;
pair<int,int> p[200010];
int sum[200010],ans[200010];
signed main(){
cin>>n>>q;
for(int i=1;i<=q;i++){
cin>>x[i];
if(vis[x[i]]){
vis[x[i]]=0;
p[last[x[i]]].second=i-1;
}else{
vis[x[i]]=1;
p[++cnt].first=i;
last[x[i]]=cnt;
}
}
for(int i=1;i<=cnt;i++){
if(p[i].second==0) p[i].second=q;
sum[p[i].first]++;
sum[p[i].second+1]--;
}
for(int i=1;i<=q;i++) sum[i]+=sum[i-1];
for(int i=1;i<=q;i++) sum[i]+=sum[i-1];
for(int i=1;i<=cnt;i++){
ans[x[p[i].first]]+=sum[p[i].second]-sum[p[i].first-1];
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
return 0;
}

不行太菜了,必须多练了

posted @   Sinktank  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2025-3-6 6:10:49 TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.
点击右上角即可分享
微信分享提示