jzoj 5796.[2018提高组] 模拟A组&省选 划分 扩展欧几里得

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多少个元素的值。

Input

第一行输入两个正整数n,M。
第二行输入M个正整数表示K[1],K[2]…..K[M]。

Output

输出1个整数,表示能确定的元素

Sample Input

【输入样例1】
3 1
2
【输入样例2】
6 2
2 3
【输入样例3】
123456789 3
5 6 9

Sample Output

【输出样例1】
1
【输出样例2】
2
【输出样例3】
10973937

Data Constraint

对于20%的数据,3 <= N <= 2000,M<=3。
对于40%的数据,3 <= N <= 5*10^6。
对于100%的数据,3 <= N <= 10^9 , 1 <= M <= 10,2 <= K[i] < N。

Hint

【样例1解释】
小A知道x的2-划分序列,即分别知道x[1]+x[2],x[3]的值。
小A可以知道x[3]的值。
【样例2解释】
小A知道x的2-划分序列,即分别知道x[1]+x[2],x[3]+x[4],x[5]+x[6] 的值。
小A知道x的3-划分序列,即分别知道x[1]+x[2]+x[3] ,x[4]+x[5]+x[6] 的值。
小A可以知道x[3],x[4]的值,个数为2.

分析:
我们发现,对于一个位置 k 能被计算出,存在 i j ,满足 k   m o d   a i = 0 k   m o d   a j = 1
也就是 k = a i x = a j y 1 ,即 a i x a j y = 1 。这个可以用扩展欧几里得求出。然后就是去重了,显然一个数可能会被不同的式子表出。可以用容斥弄掉。
例如:
k   m o d   a i 1 = 0
k   m o d   a i 2 = 0
……
k   m o d   a j 1 = 1
k   m o d   a j 2 = 1
……
这两个式子可以表示为
k   m o d   l c m ( a i 1 , a i 2 , . . . ) = 0
k   m o d   l c m ( a j 1 , a j 2 , . . . ) = 1
这样就也可以用扩展欧几里得算出。容斥系数为式子数的奇偶性。答案数显然就是 a i x [ 1 , n ] 的个数,对于最后一个位置,可以判断他是否能独立出来,如果能就直接把这位统计答案,位置数减一。注意没有 0 这个位置,这个让我wa了好久。因为每个点的状态只有等于 1 ,等于 0 ,不在方程组内,所以复杂度是 O ( 3 m ) 的。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#define LL long long

using namespace std;

LL a[20];
LL n,m,ans;
LL f[1024][1024];

LL gcd(LL x,LL y)
{
    LL r=x%y;
    while (r)
    {
        x=y;
        y=r;
        r=x%y;
    }
    return y;
}

LL getlcm(LL s)
{
    LL lcm=1;
    while (s)
    {
        LL t=s&(-s);
        LL k=trunc(log(t+0.233)/log(2));
        lcm=a[k+1]*lcm/gcd(a[k+1],lcm);
        s-=t;
    }
    return lcm;
}

LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if (b==0)
    {
        x=1; y=0;
        return a;
    }
    LL d=exgcd(b,a%b,x,y);
    LL z=x;
    x=y; y=z-a/b*y;
    return d;
}

LL calc(LL a,LL b,LL c)
{
    LL x,y;
    LL g=exgcd(a,b,x,y);
    if (c%g!=0) return 0;
    {
        LL k=b/g;
        x=x%k;
        while (x<=0) x+=k;
        LL p=n/a;
        return p/k+(x%k<=p%k);
    }
}

LL getnum(LL x)
{
    LL sum=0;
    while (x)
    {
        sum++;
        x-=x&(-x);
    }
    return sum;
}

int main()
{
    freopen("sazetak.in","r",stdin);
    freopen("sazetak.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for (LL i=1;i<=m;i++) scanf("%lld",&a[i]);
    for (LL i=1;i<=m;i++)
    {
        if (n%a[i]==1)
        {
            ans++;
            n--;
            break;
        }
    }
    for (LL s1=1;s1<(1<<m);s1++)
    {
        for (LL s2=1;s2<(1<<m);s2++)
        {
            if ((s1&s2)==0)
            {
                LL lcm1=getlcm(s1),lcm2=getlcm(s2);
                f[s1][s2]=calc(lcm1,lcm2,1);
            }
        }
    }       
    for (LL i=1;i<(1<<m);i++)
    {
        for (LL j=1;j<(1<<m);j++)
        {
            if ((i&j)==0)
            {
                LL k=getnum(i)+getnum(j);
                if (k%2==0) ans+=f[i][j];
                       else ans-=f[i][j];
            }   
        }
    }
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81568694
今日推荐