HDU 6128(乱搞)

来源:互联网 发布:手机服务器端口怎么查 编辑:程序博客网 时间:2024/04/29 03:56
Problem Description
There are n nonnegative integers a1n which are less than p. HazelFan wants to know how many pairs i,j(1i<jn) are there, satisfying 1ai+aj1ai+1aj when we calculate module p, which means the inverse element of their sum equals the sum of their inverse elements. Notice that zero element has no inverse element.
 

Input
The first line contains a positive integer T(1T5), denoting the number of test cases.
For each test case:
The first line contains two positive integers n,p(1n105,2p1018), and it is guaranteed that p is a prime number.
The second line contains n nonnegative integers a1...n(0ai<p).
 

Output
For each test case:
A single line contains a nonnegative integer, denoting the answer.
 

Sample Input
25 71 2 3 4 56 71 2 3 4 5 6
 

Sample Output
46
这个题题意是求使得 (ai+aj) 在模 p 下的逆元等于 ai 在模 p 下的逆元加上 aj 在模 p 下的逆元的 i j 的对数( i<j )
式子两边同乘( ai + aj )*ai*aj 得 ai*aj=ai*(ai+aj)+aj*(ai+aj) (mod p).
化简: ai*aj=ai^2 + aj^2 + 2*ai*aj (mod p)
0=ai^2 + aj^2 + ai*aj (mod p)
这个时候就可以用二次剩余搞了,但是我实在太弱,不会二次剩余,详情请百度大佬的做法
但是还有另外一个神奇的算法:
两边同乘 (ai-aj)
然后就变成了:0=ai^3-aj^3 (mod p)
令 bi=ai^3 (mod p), 找多少对 bi==bj
然后就好说了
不过要注意几点:
①ai=0时是没有逆元的直接continue
②当 ai==aj 时,会有ai-aj=0,所以在乘以 ai-aj 之前要判断 ai^2+aj^2+ai*aj 是否等于0,不等于0的话你乘了 ai-aj 也没用
③p=3 时要单独特判,因为 p=3 时 ai 无非就等于 0 1 2 这三个,其中 0 直接continue,aj=ai时它们不管等于1还是2,
ai^2+aj^2+ai*aj 模 p 都等于0,ai不等于aj时不管怎样目标式子模p都不等于0,与p取其他值的情况略有不同④这个方法并不优,只是好写而已,所以写法一定要优美,能省的计算就省,不然会TLE,我用不怎么优美的同一段代码交了几次才AC了两三次,2800ms,2900ms这样子,其他的都T了⑤在计算 ai*aj 的时候因为结果会炸 long long 所以要用快速乘法代码:
#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <vector>#include <queue>#include <stack>#include <cstdlib>#include <cmath>#include <map>#include <string>using namespace std;typedef long long LL;const int MOD=1e9+7;map<LL,LL>s,t;LL n,p;LL mul(LL a,LL b){    LL ans=0;    a=a%p;    b=b%p;    while(b>0)    {        if(b&1) ans=(ans+a)%p;        a=(a+a)%p;        b>>=1;    }    return ans;}LL a[200000+3];LL b[200000+3];LL c[200000+3];int main(){    int T;    scanf("%d",&T);    while(T--)    {        s.clear();        t.clear();        scanf("%lld%lld",&n,&p);        if(p==3)        {            LL a1=0,a2=0,a3=0;            for(int i=0;i<n;i++)            {                LL x;                scanf("%lld",&x);                if(x==1)a1++;                else if(x==2)a2++;            }            printf("%lld\n",a1*(a1-1)/2+a2*(a2-1)/2);            continue;        }        for(int i=0;i<n;i++)        {            scanf("%lld",&a[i]);            if(a[i]==0)continue;            LL y=mul(a[i],a[i]);            LL z=mul(a[i],y);//ai==aj时ai^2 + aj^2 + ai*aj就成了 3 * ai^2 嘛            b[i]=z;            c[i]=mul(3,y);            s[z]++;            t[a[i]]++;        }        /*        for(int i=0;i<n;i++)        {            scanf("%lld",&a[i]);            if(a[i]==0)continue;            LL z=mul(a[i],mul(a[i],a[i]));            b[i]=z;            c[i]=mul(3,mul(a[i],a[i]));            s[z]++;            t[a[i]]++;        }        之前交了好几发的同一段代码用的是这个,同一个快速乘算了两次,导致交几发才玄学过一发,其他的都T了,有兴趣的可以玩一下        */        LL ans=0;        for(int i=0;i<n;i++)        {            if(a[i]==0)continue;            {                LL z=0;                if(c[i]==0)//ai==aj时判断ai^2 + aj^2 + ai*aj为等于0的话不用减掉ai==aj的情况,直接算ai^3==aj^3即可                {                    z=s[b[i]]-1;//减掉ai它自己                }                else//ai==aj时判断ai^2 + aj^2 + ai*aj为不等于0的话要减掉ai==aj的情况再算ai^3==aj^3                {                    z=s[b[i]]-t[a[i]];                }                ans+=z;            }        }        ans/=2;        printf("%lld\n",ans);    }    return 0;}

 
emmmm........大概就这样了
本人蒟蒻,如有错误,还望指正







原创粉丝点击