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