Description:
有一个未知的序列x,长度为n。它的K-划分序列y指的是每连续K个数的和得到划分序列,y[1]=x[1]+x[2]+….+x[K],y[2]=x[K+1]+x[K+2]+….+x[K+K]….。若n不被K整除,则y[n/K+1]可以由少于K个数加起来。比如n=13,K=5,则y[1]=x[1]+…+x[5],y[2]=x[6]+….+x[10],y[3]=x[11]+x[12]+x[13]。若小A只确定x的K[1]划分序列以及K[2]划分序列….K[M]划分序列的值情况下,问她可以确定x多少个元素的值。
3 <= N <= 10^9 , 1 <= M <= 10,2 <= K[i] < N。
题解:
考虑x什么时候是确定的?
当且仅当有:
a[i]|x,a[j]|(x−1)
a
[
i
]
|
x
,
a
[
j
]
|
(
x
−
1
)
考虑有a[i]和a[j],他们能确定多少个 x x
设
p∗a[i]=1(mod a[j])
p
∗
a
[
i
]
=
1
(
m
o
d
a
[
j
]
)
p=a[i]−1(mod a[j])
p
=
a
[
i
]
−
1
(
m
o
d
a
[
j
]
)
显然 gcd(a[i],a[j])=1 g c d ( a [ i ] , a [ j ] ) = 1 才解。
a[i]−1(mod a[j]) a [ i ] − 1 ( m o d a [ j ] ) 可以用欧拉定理或者扩展欧几里得算法解决。
答案个数=⌊⌊na[i]⌋a[j]⌋+[⌊na[i]⌋ mod a[j]>=p] 答 案 个 数 = ⌊ ⌊ n a [ i ] ⌋ a [ j ] ⌋ + [ ⌊ n a [ i ] ⌋ m o d a [ j ] >= p ]
直接枚举所有的a[i]、a[j]算答案显然会有重。
那么我们用容斥搞掉重复,假设有多组限制,则分开求lcm,再求答案即可。
复杂度为 O(2m∗(m−1)) O ( 2 m ∗ ( m − 1 ) ) ,加剪枝可以拿90分。
假设分成了A、B两个集合,这两个集合显然会有重复元素, 但是我们求lcm不需要考虑重复元素。
因此直接枚举A、B集合的不同元素,容斥系数为 (−1)|A|+|B| ( − 1 ) | A | + | B | 。
下面利用二项式反演来证明这个结论:
对于一个x, x mod a[i]=0 x m o d a [ i ] = 0 的a[i]属于A集合, x mod a[j]=1 x m o d a [ j ] = 1 的a[j]属于B集合。
则x会被计算:
∑|A|i=1Ci|A|∑|B|j=1Cj|B|(−1)i+j
∑
i
=
1
|
A
|
C
|
A
|
i
∑
j
=
1
|
B
|
C
|
B
|
j
(
−
1
)
i
+
j
次
=∑|A|i=1Ci|A|∗(−1)i∑|B|j=1Cj|B|∗(−1)j
=
∑
i
=
1
|
A
|
C
|
A
|
i
∗
(
−
1
)
i
∑
j
=
1
|
B
|
C
|
B
|
j
∗
(
−
1
)
j
=((1−1)|A|−1)∗((1−1)|B|−1)
=
(
(
1
−
1
)
|
A
|
−
1
)
∗
(
(
1
−
1
)
|
B
|
−
1
)
=1
=
1
Code:
#include<cstdio>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 1e5;
int n, m, a[11], ni[11];
int gcd(int x, int y) {
return !y ? x : gcd(y, x % y);
}
int x, y;
void exgcd(int a, int b) {
if(b == 0) {x = a; y = 0; return;}
exgcd(b, a % b);
int xx = x, yy = y;
x = yy; y = xx - (a / b) * yy;
}
ll ans;
void dg(int i, ll y, ll z, int fu) {
if(y > n || z > n) return;
if(i > m) {
if(y == 1 || z == 1) return;
if(gcd(y, z) > 1) return;
exgcd(y, -z); x = ((x % z) + z) % z;
ans += fu * (n / y / z + (n / y % z >= x));
return;
}
dg(i + 1, y, z, fu);
dg(i + 1, y * a[i] / gcd(y, a[i]), z, -fu);
dg(i + 1, y, z * a[i] / gcd(z, a[i]), -fu);
}
int main() {
freopen("sazetak.in", "r", stdin);
freopen("sazetak.out", "w", stdout);
int bz1 = 1;
scanf("%d %d", &n, &m);
fo(i, 1, m) {
scanf("%d", &a[i]);
bz1 &= a[i] == 1;
}
if(bz1) {printf("%d", n); return 0;}
n --;
dg(1, 1, 1, 1);
n ++;
fo(i, 1, m) bz1 |= (n - 1) % a[i] == 0;
if(bz1) ans ++;
printf("%d", ans);
}