P9391 红草莓

发布时间 2023-08-22 20:09:29作者: One_JuRuo

洛谷 P9391 红草莓

写在前面

有超详细证明qwq!

这道题其实不难,你的感觉多半是正确的,但是证明有点麻烦,所以这篇题解,我就准备好好证明一下一些结论,所以有点长(也很基础)。

核心思想:模拟

对于一个 \(n\),每个 \(a\) 可以模拟其会染色的珠子编号,即 \(0,a,2a,3a,\cdots \pmod n\) 的值。

具体思路

每次模拟其会染色的珠子编号,判断该编号有没有标记过:若没有标记过,则标记并计入答案;若标记过了,则可以不用操作,直接跳过

那么对于一个 \(a\) 而言,要模拟几次呢?

显然,最多 \(n\) 次。(即把整个串都染完的可能)

所以我们只需要循环 \(n\) 次足以。

证明

\(b_i \equiv i\times a \pmod n\)

\(b_i = b_0\),则 \(b_{i+1} \equiv (i+1)\times a \equiv i \times a+a\equiv a \equiv b_1 \pmod n\)

同理,若 \(b_i=b_j\),则 \(b_{i+1}=b_{j+1}\)

所以由 \(b_i\) 构成的序列必然存在循环节,且循环节中不存在相同的数字。

因为 \(n\times a\equiv 0 \pmod n\),所以循环节的长度必然超过 \(n\)。并且,当且仅当,\((a,n) = 1\) 时,循环节为 \(n\)

我们再来证明一下互质,循环节为 \(n\)

\(k \times a\equiv 1 \pmod n\)(必然存在,因为 \(a\)\(n\) 互质,那么 \(k\) 就是模 \(n\) 的情况下 \(a\) 的逆元)。

那么就有 :

\(2\times k\times a\equiv 2 \pmod n\)

\(3\times k\times a\equiv 3\pmod n\)

\(\cdots\)

\((n-1)\times k\times a\equiv n-1 \pmod n\)

\(n\times k\times a\equiv 0 \pmod n\)

那么序列 \(b_i\) 中,必然存在且只存在 \(0,1,2, \cdots ,n-1\)

那么循环节必然包括且只包括这些数字,一共 \(n\) 个,所以循环节也只有 \(n\)

所以只用枚举 \(n\) 遍,就肯定能枚举完所有情况。

50分代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,a,st[500005];
int main()
{
	cin>>n>>m;
	while(m--)
	{
		cin>>a;
		int p=0,ans=0;//p记录位置
		for(int i=1;i<=n;i++)
		{
			if(!st[p])
			{
				st[p]=1;
				ans++;
			}
			p+=a;
			p%=n;
		}
		cout<<ans<<" ";
	}
	return 0;
}

很显然,这种暴力明显还过不了,所以我们还需要去优化优化。

优化

优化循环节长度

显然,很多时候,序列 \(b_i\) 的循环节长度没有 \(n\) 那么大,甚至小很多,所以我们算出具体的长度有多大。

\(d=(a,n)\)
\(a=k_1\times d\)\(n=k_2\times d\)
那么显然, \((k_1,k_2)=1\)

那么,对于 \(c_i \equiv i\times k_1 \pmod {k_2}\),循环节长度应为 \(k_2\)

显然, \(b_i \equiv i\times k_1 \times d\equiv c_i \times d \pmod {k_2}\)

所以,序列 \(b_i\) 与序列 \(c_i\) 本质上是一样的,只是数字都扩大了 \(d\) 倍,所以序列 \(b_i\) 的循环节长度也是 \(k_2\)\(\frac{n}{(a,n)}\)

优化遍历模式

我们发现,像原来那样遍历太麻烦了,每次还要操作位置,所以我们可以找到染色珠子编号的规律,让代码简洁一点。(虽然时间复杂度没有提升)

上一个优化已经得到了 \(b_i \equiv i\times k_1 \times d\equiv c_i \times d \pmod n\)

那么我们可以轻松地发现,所有要染色的珠子只可能是 \((a,n)\) 的倍数,而个数我们也知道,所以就可以直接:

for(int i=1;i<n/gcd(a,n);i++)
	if(!st[i*gcd(a,n)])
		st[i*gcd(a,n)]=1,ans++;

(虽然只是一个代码方面的小优化)

记录出现过的 \(gcd(a_i,n)\)

根据上一个优化,我们可以知道,染色的珠子编号一定是 \((a,n)\) 的倍数,所以如果这个 \(a\) 对应的 \((a,n)\) 之前出现过的话,那么这次一定一个都没法染色,可以直接跳过。

所以我们可以拿一个数组来存之前的 \((a,n)\) 出现过没。

优化特殊情况

同时呢,我们还可以拿一个变量 \(node\) 来存已经染色的珠子的数量,如果 \(node\) 已经等于 \(n\) 了,那么后面都可以直接输出 \(0\) 了。

当然了,如果有一个 \((a,n)=1\) 的话,那么可以直接输出 \(n-node\),后续也不用去计算了。

一个构想

除了以上说的那些优化方法外,我还有一个构思,但是不知道加了会变快还是变慢。

可以在输入 \(n\) 了以后,预处理一遍,得到一个数组 \(t\)\(t_i\) 代表 \(a=i\) 时,可能染色的珠子数量。

求法:

先赋初值 \(n\)

然后对 \(n\) 进行质因数分解。

每个质因数 \(i\) 对应数组的值应该为 \(\frac{n}{i}\)

然后像线性筛一样去算对应的值,显然,我们有 \(t_{i \times j}=(t_i,t_j)\)

证明:

对于 \(t_i\),我们可以把 “1” 分为 \(t_i\) 份,同理,对于 \(t_j\),我们也可以把 “1” 分为 \(t_j\) 份。

那么, \(\frac{k \times \frac{t_i}{(t_i,t_j)}}{t_i}=\frac{k}{(t_i,t_j)}\)\(\frac{k \times \frac{t_j}{(t_i,t_j)}}{t_j}=\frac{k}{(t_i,t_j)}\) 是一样的。

如果把 \(\frac{k}{t_i}\) 当作是 \(a\)\(i\) 时,可以染色的珠子在整串珠子间的分布位置为 \(\frac{k}{t_i}\)。那么, \(\frac{k}{t_i}\) 的范围应该是 \((0,1)\)

所以, \(k\) 可以取 \((0,(t_i,t_j)-1)\),所以 \(k\)\((t_i,t_j)\) 种值,也就是共同都可以染色的珠子数为 \((t_i,t_j)\)

那么,对于 \(a=i\)\(a=j\) 都能染色的珠子, \(a=i \times j\) 时,也应该可以染这些珠子。

所以 \((t_i,t_j)\) 就应该是 \(t_{i \times j}\) 的值。

这样的话,我们就可以直接调用其个数,并且也可以通过 \(\frac{n}{t_i}\) 求得 \((i,n)\)

注意

尽管有些情况可以直接跳过,但是数据要读完啊,不要把输入放到 continue 后面了。

这道题代码方面很简单,前面也给出了个 \(50\) 分的代码,拿那个改一改也能过,而且这道题数据有点水,只优化一两个上面说的点都能过,所以这里就不放最终代码了。