题目传送门:P8178 「EZEC-11」Sequence
【题意】
-
已知数列 f f f 满足 f i = a i ⋅ f i − 1 + b i ( i ≥ 1 ) f_i=a_i\cdot f_{i-1}+b_i(i\ge1) fi=ai⋅fi−1+bi(i≥1) ,
-
问是否存在非负整数 f 0 f_0 f0 ,使得 f i f_i fi 为 质数 p i p_i pi( 1 ≤ i ≤ n 1\le i \le n 1≤i≤n )的倍数。
-
本题有多组测试数据,若存在满足条件的 f 0 f_0 f0 则输出
Yes
,否则输出No
。 -
对于 100 % 100\% 100% 的数据, 1 ≤ T ≤ 10 1\le T\le10 1≤T≤10 , 1 ≤ n ≤ 1 0 3 1\le n\le10^3 1≤n≤103 , 0 ≤ a i , b i ≤ 1 0 9 0\le a_i,b_i\le 10^9 0≤ai,bi≤109 , 2 ≤ p i ≤ 1 0 9 2\le p_i\le 10^9 2≤pi≤109 , p p p 为质数
【分析】
- 前置条件:看懂题目,已经会打暴力
- 最先想到的肯定是枚举
f
0
f_0
f0 ,若有满足的则输出
Yes
,否则输出No
,但这样的时间复杂度为 O ( max ( f 0 , l c m ( p 1 , 2 , … , n ) ) ⋅ n 2 ) O ( \max(f_0,lcm(p_{1,2,\ldots,n}))\cdot n^2 ) O(max(f0,lcm(p1,2,…,n))⋅n2) - 代码类似这样:
while(f[0]<gcd(all:p[1-n]))
{
int flag=0;
for(int i=1;i<=n;i++)
{
f[i]=a[i]*f[i-1]+b[i];
if(f[i]%p[i])
{
flag=1;
break;
}
}
if(!flag)
{
printf("%d",f[0]);
}
++f[0];
}
- 看到
n
2
≤
1
0
6
n^2\le10^6
n2≤106 和输出
Yes
或No
想到这是一道数学结论题,考虑去掉 max ( f 0 , l c m ( p 1 , 2 , … , n ) ) \max(f_0,lcm(p_{1,2,\ldots,n})) max(f0,lcm(p1,2,…,n)) 。 - 想到可以将 f i f_i fi 用 c i ⋅ f 0 + d i c_i \cdot f_0+d_i ci⋅f0+di 表示,改变 f 0 f_0 f0 后可以用 O ( 1 ) O(1) O(1)的时间复杂度直接求出 f i f_i fi 。
- 先不考虑 p i p_i pi ,看一下数组 c c c与数组 d d d 的规律:
i i i | c c c | d d d |
---|---|---|
0 | 0 | 0 |
1 | a 1 a_1 a1 | b 1 b_1 b1 |
2 | a 1 ⋅ a 2 a_1 \cdot a_2 a1⋅a2 | ( b 1 ) ⋅ a 2 + b 2 (b_1) \cdot a_2+b_2 (b1)⋅a2+b2 |
3 | a 1 ⋅ a 2 ⋅ a 3 a_1 \cdot a_2 \cdot a_3 a1⋅a2⋅a3 | ( b 1 ⋅ a 1 + b 2 ) ⋅ a 3 + b 3 (b_1 \cdot a_1+b_2) \cdot a_3+b_3 (b1⋅a1+b2)⋅a3+b3 |
4 | a 1 ⋅ a 2 ⋅ a 3 ⋅ a 4 a_1 \cdot a_2 \cdot a_3 \cdot a_4 a1⋅a2⋅a3⋅a4 | ( ( b 1 ⋅ a 1 + b 2 ) ⋅ a 3 + b 3 ) ⋅ a 4 + b 4 ((b_1 \cdot a_1+b_2) \cdot a_3+b_3) \cdot a_4+b_4 ((b1⋅a1+b2)⋅a3+b3)⋅a4+b4 |
- 眼尖的同学应该已经发现了 c , d c,d c,d 数组的规律:
- c i = c i − 1 ⋅ a i c_i=c_{i-1}\cdot a_i ci=ci−1⋅ai。
- d i = d i − 1 ⋅ a i + b i d_i=d_{i-1}\cdot a_i + b_i di=di−1⋅ai+bi。
- 于是代码变成了这样:
c[0]=1;
for(int i=1; i<=n; i++) {
c[i]=c[i]*a[j];
d[i]=d[i]*a[j]+b[j];
}
- 很短吧~
- 但是! c i , d i c_i,d_i ci,di 可能变得很大很大,所以需要当场 m o d p i \bmod p_i modpi
- c i ⋅ f 0 + d i ≡ c i m o d p i ⋅ f 0 + d i m o d p i ( m o d p i ) c_i \cdot f_0+d_i\equiv c_i\bmod p_i\cdot f_0+d_i\bmod p_i(\bmod p_i) ci⋅f0+di≡cimodpi⋅f0+dimodpi(modpi)
- 考虑 p i p_i pi, f i f_i fi 为 p i p_i pi 的倍数,即 f i m o d p i = 0 f_i\bmod p_i=0 fimodpi=0 ,使 c i = c i m o d p i c_i=c_i \bmod p_i ci=cimodpi , d i = d i m o d p i d_i=d_i \bmod p_i di=dimodpi。
- 但是, p i p_i pi 不一定相同!所以针对每个 p i p_i pi用一个 O ( n ) O( n ) O(n)的循环求出 c i , d i c_i,d_i ci,di 。
- 于是用 O ( n 2 ) O( n^2 ) O(n2)的预处理将这题转变成了另一个问题。
【简要题意】
给出 n n n 个 c i c_i ci , d i d_i di , p i p_i pi ,求是否有满足所有公式 c i ⋅ x + d i ≡ 0 ( m o d p i ) c_i\cdot x+d_i\equiv 0(\bmod p_i) ci⋅x+di≡0(modpi) 的通项 x x x 。
很容易想到应该将 x x x 的系数化为1。- 但是!若 c i = 0 c_i=0 ci=0 ,就无法将 x x x 的系数化为1,但是!我们可以直接判断是否存在解了!
- 考虑如何判断(
这个应该比较简单),直接判断 d i m o d p i d_i\bmod p_i dimodpi 是否等于0,若不等于,则可以判断绝对是不存在解的,若等于则可以不考虑 i i i ,因为无论 x x x 等于几都必定有解 - 那么若 c i c_i ci 不等于0,则将 d i d_i di 除上一个 c i c_i ci ,但我们已将 c i , d i m o d p i c_i,d_i\bmod p_i ci,dimodpi ,那么考虑求 p i p_i pi 的逆元为 p x i px_i pxi ,这里就不做赘述
- 将 d i = d i ⋅ p x i m o d p i d_i=d_i\cdot px_i\bmod p_i di=di⋅pximodpi
- 于是用 O ( n log n ) O( n\log n ) O(nlogn)的预处理又将这题转变成了另一个问题。
【简要题意】
给出 n n n 个 d i d_i di , p i p_i pi ,求是否有满足所有公式 ( x + d i ) m o d p i = 0 ( d i ≥ 0 ) (x+d_i)\bmod p_i=0(d_i\ge 0) (x+di)modpi=0(di≥0) 的通项 x x x。
【分析】
- 公式 ( x + d i ) m o d p i = 0 (x+d_i)\bmod p_i=0 (x+di)modpi=0 即 x + d i x+d_i x+di 为 p i p_i pi 的倍数
- 所以化为 x = − d i ( f ( i ) ) x=-d_i(f(i)) x=−di(f(i)) , f ( i ) = ( i m o d p i + p i ) m o d p i f(i)=(i\bmod p_i+p_i)\bmod p_i f(i)=(imodpi+pi)modpi
- 使 f i = f ( d i ) f_i=f(d_i) fi=f(di)
- 而保证 p i p_i pi 为质数,若 p i p_i pi 不同则一定有解,若 p i = p j p_i=p_j pi=pj 且 f i f_i fi 不等于 f j f_j fj ,则必定无解,否则有解
【总结】
- 以上全部判断完后若还全部满足则可以输出
Yes
了
【代码】
#include<cstdio>
#include<unordered_map>
#define N 1009
#define int long long
using namespace std;
unordered_map<int,int>ma;
int t,n,flag;
int a[N],b[N],c[N],d[N],p[N];
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x=1;y=0;
return a;
}
int q=a/b,r=a%b,ex;
ex=exgcd(b,r,y,x);
y-=q*x;
return ex;
}
main() {
scanf("%lld",&t);
while(t--) {
flag=0;
scanf("%lld",&n);
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(int i=1; i<=n; i++)
scanf("%lld",&b[i]);
for(int i=1; i<=n; i++)
scanf("%lld",&p[i]);
for(int i=1; i<=n; i++) {
c[i]=1;
d[i]=0;
for(int j=1; j<=i; j++) {
c[i]=c[i]*a[j]%p[i];
d[i]=(d[i]*a[j]+b[j])%p[i];
}
/*求c[i]与d[i],因为p[i]不同,所以针对每个p[i]都1~i枚举一遍
虽然时间复杂度变为了O(n^2),但没有超时~~~
而且不需要打高精了*/
if(!c[i]&&d[i]) {
puts("No");
flag=1;
break;
}
if(!c[i]) {
p[i]=1e9;
//用一个非质数来代替p[i]防止与真正的p[i]重复
d[i]=1e9;
continue;
}
int t;
exgcd(c[i],p[i],c[i],t);
d[i]=(((-d[i])%p[i]+p[i])%p[i])*c[i]%p[i];
d[i]=(d[i]+p[i])%p[i];
//将x的系数化为1
}
if(!flag) {
for(int i=1; i<=n; i++) {
if(ma[p[i]]&&ma[p[i]]!=d[i]+1) {
puts("No");
break;
} else {
ma[p[i]]=d[i]+1;
//+1是为了防止d[i]为0误判为没有存过
}
if(i==n) {
puts("Yes");
}
}
}
ma.clear();
}
return 0;
}