CFGym 102354A Square Root Partitioning(二次剩餘)
阿新 • • 發佈:2020-12-25
題意:給出一個長度為n的序列a,要使得\(\sqrt{a1}\)\(\pm\)\(\sqrt{a2}\)\(\pm\)....\(\pm\)\(\sqrt{an}\)=0,求滿足情況的加減種數。
題解:我們可以用ai的二次剩餘代替\(\sqrt{ai}\),但在某些模p下ai可能是非二次剩餘,所以我們要找到一個使所有a都滿足二次剩餘的p,根據勒讓德符號\(a^\frac{p-1}{2}\) \(\equiv\) 1(mod p)就能使ai在模p情況下使二次剩餘\(a^\frac{p+1}{2}\) \(\equiv\) a(mod p),所以a的二次剩餘是\(\frac{p+1}{4}\),所以我們找的p必須是(p+1)%4==0的奇素數,取得大一點好一點,避免出題人卡你,將序列分為前半部分和後半部分,把前半部分的值存起來,後半部分的每一次操作加上前半部分存起來的值。
原題連結
#include<stdio.h> #include<string.h> #include<map> using namespace std; #define ll long long const ll mod=10000000012388587ll; ll gc(ll a,ll x) { ll ans=0; while(x!=0) { if(x%2==1) ans=(ans+a)%mod; a=(a+a)%mod; x>>=1; } return ans; } ll quickpow(ll a,ll b){ ll ans=1; while(b!=0){ if(b%2==1)ans=gc(ans,a); b=b/2; a=gc(a,a); } return ans; } char s[100010]; ll a[50]; map<ll,int>cnt; int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%s",s+1); int len=strlen(s+1); ll temp=0; for(int j=1;j<=len;j++){ temp=(temp*10%mod+s[j]-'0')%mod; } a[i]=quickpow(temp,(mod+1)/4); } int c=n/2; for(int i=0;i<=(1<<(c))-1;i++){ ll now=0; for(int j=0;j<c;j++){ if((i&(1<<j))==0){ now=(now+a[j])%mod; } else{ now=(now-a[j]+mod)%mod; } } cnt[now]++; } ll ans=0; for(int i=0;i<=(1<<(n-c))-1;i++){ ll now=0; for(int j=0;j<n-c;j++){ if((i&(1<<j))==0){ now=(now+a[c+j])%mod; } else{ now=(now-a[c+j]+mod)%mod; } } ans=ans+cnt[now]; } printf("%lld\n",ans/2); }