2017多校第7场 HDU 6128 Inverse of sum 推公式或者二次剩余

Posted Lsxxxxxxxxxxxxx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017多校第7场 HDU 6128 Inverse of sum 推公式或者二次剩余相关的知识,希望对你有一定的参考价值。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6128

题意:给你n个数,问你有多少对i,j,满足i<j,并且1/(ai+aj)=1/ai+1/a在%p意义下。

解法:官方题解说是用二次剩余来解,但是我并不会这玩意了。在网上看到一位大佬没有二次剩余直接通过推公式做出了这题,真是神奇。http://www.cnblogs.com/bin-gege/p/7367337.html  将式子通分化简后可得(ai2+aj2+ai*aj)%p=0 。然后两边同时将两边乘(ai-aj),化简可得(ai3-aj3)%p=0。那么直接计算满足这个等式的pair的对数就可以了吗?不是。我们还要考虑到a[i]=a[j]的时候,也就是a[i]*a[i]+a[i]*a[i]+a[i]*a[i]=0modp是不满足条件的,但是我们直接计算上面那个式子会把满足这个关系的式子也算进去,所以我们需要把满足a[i]=a[j]并且3*a[i]*a[j]>0的这些对数减掉。我这个代码跑了2900多ms,所以这题还是顶二次剩余吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+4;
int T,n;
LL p,a[maxn];
map<LL,int>cnt;
map<LL,int>cnt2;
inline LL quick_mul(LL a,LL n,LL m)
{
    LL ans=0;
    while(n)
    {
        if(n&1) ans=(ans+a)%m;
        a=(a+a)%m;
        n>>=1;
    }
    return ans;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%lld", &n,&p);
        for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
        LL ans=0;
        cnt.clear();
        cnt2.clear();
        for(int i=1; i<=n; i++){
            if(!a[i]) continue;
            if(quick_mul(3,quick_mul(a[i],a[i],p),p)) ans-=cnt2[a[i]];
            LL t = quick_mul(quick_mul(a[i],a[i],p),a[i],p);
            ans += cnt[t];
            cnt[t]++;
            cnt2[a[i]]++;
        }
        printf("%lld\\n", ans);
    }
    return 0;
}

 

以上是关于2017多校第7场 HDU 6128 Inverse of sum 推公式或者二次剩余的主要内容,如果未能解决你的问题,请参考以下文章

HDU多校2017第7场

2017多校第10场 HDU 6172 Array Challenge 猜公式,矩阵幂

2017多校第6场 HDU 6105 Gameia 博弈

2017多校第10场 HDU 6181 Two Paths 次短路

2017多校第10场 HDU 6180 Schedule 贪心,multiset

2017多校第9场 HDU 6170 Two strings DP