1. 程式人生 > >2018.12.30【NOIP訓練】【SCOI2018】Numazu 的蜜柑(二次剩餘)

2018.12.30【NOIP訓練】【SCOI2018】Numazu 的蜜柑(二次剩餘)

題面傳送門


解析:

直接解方程可以得到 a u a v

A ± A 2 4 B
2 ( m o d p )
a_u\equiv a_v\frac{-A\pm\sqrt{A^2-4B}}{2}\pmod p

利用二次剩餘直接解出 d e t A 2 4 B ( m o d p ) det\equiv \sqrt{A^2-4B}\pmod p

然後分情況討論。

如果有解,一邊dfs樹的時候一邊用map 統計一下就好了。

如果無解,dfs只需要記錄它上方有多少個祖先的權值為0,然後看它自己的權值是否為0,那麼它和祖先就能組成合法點對,記錄就行了。

另外,本校OJ上的資料是我造的。。。https://blog.csdn.net/zxyoi_dreamer/article/details/85392259


程式碼:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc put_char
#define puts put_s
#define cs const

namespace IO{
	namespace READONLY{
		cs int Rlen=1<<18|1;
		char buf[Rlen],*p1,*p2;
	}
	inline char get_char(){
		using namespace READONLY;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	inline ll getint(){
		re ll num;
		re char c;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

inline ll mul(cs ll &a,cs ll &b,cs ll &mod){return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;}
namespace Find_root{
	inline ll quickpow(ll a,ll b,cs ll &mod,ll res=1){
		for(;b;b>>=1,a=mul(a,a,mod))if(b&1)res=mul(res,a,mod);
		return res;
	}
	ll W,Mod;
	struct Complex{
		ll x,y;
		Complex(cs ll &_x=0,cs ll &_y=0):x(_x),y(_y){}
		inline friend Complex operator*(cs Complex &a,cs Complex &b){
			return Complex(
				(mul(a.x,b.x,Mod)+mul(mul(a.y,b.y,Mod),W,Mod))%Mod,
				(mul(a.x,b.y,Mod)+mul(a.y,b.x,Mod))%Mod
			);
		}
	};
	
	inline Complex quickpow(Complex a,ll b){
		re Complex res(1,0);
		for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
		return res;
	}
	
	inline ll solve(ll a,ll p){
		a%=p;if(a==0)return 0;
		if(quickpow(a,(p-1)>>1,p)==p-1)return -1;
		re ll b;
		Mod=p;
		while(true){
			b=rand()%p;
			W=(mul(b,b,p)-a+p)%p;
			if(quickpow(W,(p-1)>>1,p)==p-1)break;
		}
		return quickpow(Complex(b,1),(p+1)>>1).x;
	}
}

int n;
ll p,A,B;

cs int N=100005;
vector<int> edge[N];
inline void addedge(cs int &u,cs int &v){
	edge[u].push_back(v);
}

ll a[N];
ll a1,a2,ans;
tr1::unordered_map<ll,int> cnt;

inline void dfs1(int u){
	ans+=cnt[a[u]];
	re ll v1=mul(a[u],a1,p),v2=mul(a[u],a2,p);
	v1==v2?++cnt[v1]:(++cnt[v1],++cnt[v2]);
	for(int re e=0;e<edge[u].size();++e)
	dfs1(edge[u][e]);
	v1==v2?--cnt[v1]:(--cnt[v1],--cnt[v2]);
}

int now;
inline void dfs2(int u){
	if(!a[u])ans+=now,++now;
	for(int re e=0;e<edge[u].size();++e)
	dfs2(edge[u][e]);
	if(!a[u])--now;
}

signed main(){
	n=getint();p=getint();A=getint();B=getint();
	for(int re i=1;i<=n;++i)a[i]=getint();
	for(int re i=2;i<=n;++i)addedge(getint(),i);
	ll det=Find_root::solve((mul(A,A,p)-4*B%p+p)%p,p);
	if(det==-1)dfs2(1);
	else{
		ll inv2=p-(p/2);
		a1=mul((det-A+p)%p,inv2,p);
		a2=mul((-det-A+p+p)%p,inv2,p);
		dfs1(1);
	}
	printf("%lld",ans);
	return 0;
}