HDU-6128 Inverse of sum(二次剩余/公式)

来源:互联网 发布:php分割价格函数 编辑:程序博客网 时间:2024/04/29 15:47

HDU-6128
n个小于质数p的非负整数a1n,你想知道有多少对i,j(1i<jn)i,j(1i<jn),使得模p意义下1ai+aj1ai+1aj​即这两数的和的逆元等于它们逆元的和,注意零元没有逆元。1n105,2p1018

式子推一下可得当且仅当模意义下两数之比为1+32132。于是求取3pt,当t不存在时无解. 则t=3modpt2=3modp
则比值为aiaj=1±t2的数对符合要求。对于每一个aj,我们找出ai=1±t2aj的数量,就能统计出答案。

  • 模数为2的时候特判,因为有除2操作,需要2的逆元存在。
  • 注意 1+t2=1+t2的情况
  • 忽略为0的输入
  • 模数很大,使用o1乘。

另一种方法是推导到a2+b2+ab=0
a=b,3a2=0,只有p=3时有正数解,且解为a=1,2
ab,两边乘ab,得到a3=b3modp, 查找符合等式的数量即可。

下面是第一种方法的代码

#include <bits/stdc++.h>using namespace std;const int MAXN=1e5+7;long long mul(long long x,long long y,long long mod) //O1乘{    return (x*y-(long long)(x/(long double)mod*y+1e-3)*mod+mod)%mod;}long long qpow(long long a,long long b,long long c){    long long r=1;    while(b)    {        if(b&1)            r=mul(r,a,c);        b>>=1;        a=mul(a,a,c);    }    return r;}long long modsqr(long long a,long long n)//n为质数{    long long b,k,i,x;    a=(a%n+n)%n;    if(a==0)//a为0时return        return 0;    if(n==2)        return a%n;    if(qpow(a,(n-1)/2,n)==1)//判断有无二次剩余    {        if(n%4==3)//-1是二次非剩余            x=qpow(a,(n+1)/4,n);        else        {            for(b=1;qpow(b,(n-1)/2,n)==1;b=1+rand()%(n-1));            i=(n-1)/2;            k=0;            do            {                i/=2,k/=2;                if((mul(qpow(a,i,n),qpow(b,k,n),n)+1)%n==0)                    k+=(n-1)/2;            }            while(i%2==0);            x=mul(qpow(a,(i+1)/2,n),qpow(b,k/2,n),n);        }        if(x*2>n)            x=n-x;        return x;    }    return -1;}vector<long long> val;vector<long long> check;int main(){    int T;    scanf("%d",&T);    while(T--)    {        long long n,p,ta;        scanf("%lld%lld",&n,&p);        val.clear();        for(int i=0;i<n;i++)        {            scanf("%lld",&ta);            if(ta)                val.push_back(ta);        }        n=val.size();        long long ans=0;        if(p==2)            ans=1ll*n*(n-1)/2;        else        {            long long t=modsqr(-3,p);            if(t==-1)            {                printf("0\n");                continue;            }            long long inv2=(p+1)>>1;            check.clear();            check.push_back(mul((-1+p+t)%p,inv2,p));            check.push_back(mul((-1+p-t)%p,inv2,p));            sort(val.begin(),val.end());            if(check[0]==check[1])                check.pop_back();            long long tmp;            for(int i=0;i<n;i++)            {               for(auto item:check)               {                   tmp=mul(item,val[i],p);                   ans+=upper_bound(val.begin(),val.begin()+i,tmp)-lower_bound(val.begin(),val.begin()+i,tmp);               }            }        }        printf("%lld\n",ans);    }    return 0;}
原创粉丝点击