HDU 6128 Inverse of sum(二次剩余)

201 篇文章 10 订阅

Description

n 个小于p的非负整数 a1,...,n ,问有多少对 (i,j)(1i<jn) p 在意义下满足1ai+aj1ai+1aj,即这两个数的和的逆元等于这两个数的逆元的和,注意0没有逆元

Input

第一行一整数 T 表示用例组数,每组用例首先输入一整数n表示序列长度和一素数 p 表示模数,之后输入n个非负整数 a1,...,n(1T5,1n2×105,2p1018,0a1,...,n<p)

Output

输出满足条件的 (i,j)(1i<jn) 对数

Sample Input

2
5 7
1 2 3 4 5
6 7
1 2 3 4 5 6

Sample Output

4

6

Solution1

1ai+aj1ai+1aj 通分得 a2i+aiaj+a2j0

aiaj,a3ia3j(aiaj)(a2i+aiaj+a2j)0 ,用一个 map 存所有的 a3i%p 的值,另一个 map 存所有 ai 的值( ai0 ),在第一个 map 里计数,在第二个 map 里减掉 ai=aj 的情况即可

ai=aj ,由 12ai2ai 3ai0 ,注意到 0ai<p ,故只有 p=3 ai=aj 对答案有贡献,故在上述的计数过程中特判 p=3 的情况即可

Code1

#include<cstdio>
#include<map>
using namespace std;
typedef long long ll;
ll mod_mul(ll a,ll b,ll p)
{
    ll ans=0;
    while(b)
    {
        if(b&1)ans=(ans+a)%p;
        a=(a+a)%p;
        b>>=1;
    }
    return ans;
}
int T,n;
ll p;
map<ll,int>a,m;
map<ll,int>::iterator it;
ll C(int n)
{
    return (ll)n*(n-1)/2;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        a.clear(),m.clear();
        scanf("%d%I64d",&n,&p);
        for(int i=1;i<=n;i++)
        {
            ll t;
            scanf("%I64d",&t);
            if(t)
            {
                a[t]++;
                t=mod_mul(mod_mul(t,t,p),t,p);
                m[t]++;
            }
        }
        ll ans=0;
        for(it=m.begin();it!=m.end();it++)
            ans+=C(it->second);
        if(p>3)
            for(it=a.begin();it!=a.end();it++)
                ans-=C(it->second);
        printf("%I64d\n",ans);
    }
    return 0;
}

Solution2

1ai+aj1ai+1aj 通分得 a2i+aiaj+a2j0 ,进一步有 (aiaj)2+aiaj+10 ,解此方程有 aiaj1±32

故问题转化为 p3 是否是模 p 二次剩余(p=2显然无解),如果不是则无解,如果是,设 d2p3

d=d12d1+p2dd

枚举 aj ,满足条件的 ai aj×d ,拿 map ai 每次计数累加答案即可

注意到如果 d=1 说明 ai=aj 也对答案有贡献,但在计数的时候会把 ai=ai 也算进去,所以要从答案中去掉这些不合法方案,即减掉 ai0 的个数

同时注意到,若 d21 ,对于一对合法解 (ai,aj) ,在计算 ai aj 的时候都被累加到答案中,故此时答案要除二

Code2

#include<cstdio>
#include<cstdlib>
#include<map>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace fastIO 
{
    #define BUF_SIZE 100000
    //fread -> read
    bool IOerror=0;
    inline char nc() 
    {
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if(p1==pend) 
        {
            p1=buf;
            pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if(pend==p1) 
            {
                IOerror=1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) 
    {
        return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
    }
    inline void read(int &x) 
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
    }
    inline void readlld(ll &x) 
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10ll+ch-'0');
    }
    inline void readc(char &x)
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        x=ch;
    }
    inline void reads(char *x)
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        int n=0;
        while(1)
        {
            x[n++]=ch;
            if(blank(ch=nc()))break;
        }
        x[n]='\0';
    }
    #undef BUF_SIZE
};
using namespace fastIO;
#define maxn 100005
ull Rand()
{
    ull a=rand(),b=rand(),c=rand(),d=rand();
    return a*b*c*d; 
}
typedef long long ll;
typedef long double ld;
ll Mul(ll a,ll b,ll p)
{
    return (a*b-(ll)(a/(ld)p*b+1e-3)*p+p)%p;
}
ll Pow(ll a,ll b,ll p)
{
    ll ans=1;;
    while(b)
    {
        if(b&1)ans=Mul(ans,a,p);
        a=Mul(a,a,p);
        b>>=1;
    }
    return ans;
}
//表示x+y*sqrt(w)
struct T
{
    ll x,y;
    T(){};
    T(ll _x,ll _y){x=_x,y=_y;}
};
ll w;
T T_Mul(T a,T b,ll p)
{
    T ans;
    ans.x=(Mul(a.x,b.x,p)+Mul(Mul(a.y,b.y,p),w,p))%p;
    ans.y=(Mul(a.x,b.y,p)+Mul(a.y,b.x,p))%p;
    return ans;
}
T T_Pow(T a,ll b,ll p)
{
    T ans=T(1,0);
    while(b)
    {
        if(b&1)ans=T_Mul(ans,a,p);
        a=T_Mul(a,a,p);
        b>>=1;
    }
    return ans;
}
ll Solve(ll a,ll p)//求解x^2=a(mod p) 
{
    a%=p;
    if(!a)return 0;
    if(p==2)return 1;
    if(Pow(a,(p-1)/2,p)==p-1)return -1;
    ll b;
    while(1)
    {
        b=Rand()%p;
        w=(Mul(b,b,p)-a+p)%p;
        if(Pow(w,(p-1)/2,p)==p-1)break;
    } 
    T ans=(T){b,1};
    ans=T_Pow(ans,(p+1)/2,p);
    return ans.x;
    //另一个解为p-ans.x 
}
int Case,n;
ll a[maxn],p;
map<ll,int>m;
int main()
{
    srand(time(0));
    //freopen("1009.in","r",stdin);
    read(Case);
    //scanf("%d",&Case);
    while(Case--)
    {
        m.clear();
        //scanf("%d%I64d",&n,&p);
        read(n),readlld(p);
        int num=0;
        for(int i=1;i<=n;i++)
        {
            readlld(a[i]);
            //scanf("%I64d",&a[i]);
            if(a[i])num++,m[a[i]]++;
        }
        ll d=Solve(p-3,p);
        if(p==2||d==-1)
        {
            printf("0\n");
            continue;
        }
        if(d&1)d=(d-1)>>1;
        else d=(d-1+p)>>1;
        ll ans=0;
        for(int i=1;i<=n;i++)ans+=m[Mul(a[i],d,p)];
        if(d==1)ans-=num;
        if(Mul(d,d,p)==1)ans/=2;
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值