description
- 有mm个数a[1..m],对于a[i]a[i],我们可以知道∑k∗a[i]i=(k−1)∗a[i]+1x[i](k∗a[i]<=n)∑i=(k−1)∗a[i]+1k∗a[i]x[i](k∗a[i]<=n)以及
- 前n个数的和,问我们能确定几个x[i]x[i]
40points
- sum[i]=∑ni=1x[i]sum[i]=∑i=1nx[i]
- 如果知道sum[i]和sum[i−1],那么我们就可以确定x[i]sum[i]和sum[i−1],那么我们就可以确定x[i]
- 结论显然
- 如果知道区间[l..r],区间[l..i−1],区间[i+1..r]各自的和,那也可以知道x[i]如果知道区间[l..r],区间[l..i−1],区间[i+1..r]各自的和,那也可以知道x[i]
- 但区间[i+1..r]前面区间一定有区间[x..i],所以结论证毕但区间[i+1..r]前面区间一定有区间[x..i],所以结论证毕
- 所以我们对每个a[i]把他的倍数bz一下,这说明他的倍数的前缀和可以确定所以我们对每个a[i]把他的倍数bz一下,这说明他的倍数的前缀和可以确定
- O(∑mi=1⌊na[i]⌋+n)O(∑i=1m⌊na[i]⌋+n)
100points
- 对于x能否被确定,显然可以列方程对于x能否被确定,显然可以列方程
- x mod a[i]=0x mod a[i]=0
- x mod a[j]=1x mod a[j]=1
- 是不是列m2个方程,求可行解就可以了?!!是不是列m2个方程,求可行解就可以了?!!
- 会算重!!!会算重!!!
- 那么我们就枚举第i个数是选结果为0的方程(放A集合),结果为1的方程(放B集合),还是不选那么我们就枚举第i个数是选结果为0的方程(放A集合),结果为1的方程(放B集合),还是不选
- 然后列方程后对于结果相同的,可以用lcm来合并然后列方程后对于结果相同的,可以用lcm来合并
- 最后用拓展gcd算出解的个数∗(−1)|A|+|B|最后用拓展gcd算出解的个数∗(−1)|A|+|B|
- 证如果x合法,只会算1次证如果x合法,只会算1次
- ∑|A|i=1Ci|A|∑|B|i=1Ci|B|(−1)i+j∑i=1|A|C|A|i∑i=1|B|C|B|i(−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
using namespace std;
const ll maxN=20;
ll n,m,i,j,ii,jj,sum1,ans,a[maxN],bz;
ll gcd(ll x,ll y){
return (x%y==0)?y:gcd(y,x%y);
}
ll lcm(ll x,ll y){
i=gcd(x,y);
return x/i*y;
}
void exgcd(ll xx,ll yy){
if (yy==0){
i=1,j=0;
return;
}
exgcd(yy,xx%yy);
ii=i,jj=j;
i=-jj,
j=-ii-(xx/yy)*jj;
}
void dfs(ll t,ll x,ll y,ll sum){
if (gcd(x,y)!=1) return;
if (x>n || y>n) return;
if (t>m){
if (x==1 || y==1) return;
exgcd(x,y);
i=(i%y+y)%y;
j=n/x;
if (j<i){
return;
}
sum1=(j-i)/y;
sum1+=(j>=i);
ans+=sum*sum1;
if (j*x==n && (j-i)%y==0){
bz=0;
}
return;
}
dfs(t+1,lcm(a[t],x),y,-sum);
dfs(t+1,x,lcm(a[t],y),-sum);
dfs(t+1,x,y,sum);
}
int main(){
freopen("sazetak.in","r",stdin);
freopen("sazetak.out","w",stdout);
scanf("%lld%lld",&n,&m);
fo(i,1,m) scanf("%lld",&a[i]);
bz=1;
dfs(1,1,1,1);
fo(i,1,m)
if ((n-1)%a[i]==0) ans+=bz;
printf("%lld",ans);
}