HDU 6128(乱搞)

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
  
  
2 5 7 1 2 3 4 5 6 7 1 2 3 4 5 6
 

Sample Output
  
  
4 6
这个题题意是求使得 (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........大概就这样了
本人蒟蒻,如有错误,还望指正







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值