JZOJ5796. 2018.08.10【2018提高组】模拟A组&省选 划分(二项式反演容斥+拓展GCD)

本文探讨了一种通过构建方程组并使用数论方法解决特定数列中未知项确定性的算法。通过对给定数列的倍数关系进行分析,利用数学中的最小公倍数(lcm)与最大公约数(gcd)等概念,有效地确定了数列中能够被唯一确定的项的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

description

  • mm个数a[1..m],对于a[i]a[i],我们可以知道ka[i]i=(k1)a[i]+1x[i]ka[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[i1],x[i]sum[i]和sum[i−1],那么我们就可以确定x[i]
  • 结论显然
  • [l..r],[l..i1],[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一下,这说明他的倍数的前缀和可以确定
  • Omi=1na[i]+nO(∑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个方程,求可行解就可以了?!!
  • 会算重!!!
  • i0(A)1B那么我们就枚举第i个数是选结果为0的方程(放A集合),结果为1的方程(放B集合),还是不选
  • lcm然后列方程后对于结果相同的,可以用lcm来合并
  • gcd(1)|A|+|B|最后用拓展gcd算出解的个数∗(−1)|A|+|B|
  • x1证如果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
  • =((11)|A|1)((11)|B|1)=((1−1)|A|−1)∗((1−1)|B|−1)
  • =1=1
#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
#define fo(i,a,b) for(i=a;i<=b;i++)
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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值