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/aj 在%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 推公式或者二次剩余的主要内容,如果未能解决你的问题,请参考以下文章
2017多校第10场 HDU 6172 Array Challenge 猜公式,矩阵幂
2017多校第10场 HDU 6181 Two Paths 次短路