1. 程式人生 > 實用技巧 >CFGym 102354A Square Root Partitioning(二次剩餘)

CFGym 102354A Square Root Partitioning(二次剩餘)

題意:給出一個長度為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);
}