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;
}
posted @ 2024-10-28 19:48  Jerrycyx  阅读(3)  评论(0编辑  收藏  举报