「SCOI 2018」Numazu 的蜜柑

传送门


problem

给定一个 n n n 的点的有根树(根为 1 1 1),点 u u u 有权值 a u a_u au。统计满足下列条件的点对 ( u , v ) (u,v) (u,v) 的数量:

  • v v v u u u 的父亲。
  • a u   2 + A a u a v + B a v   2 ≡ 0 ( m o d p ) a_u^{\,2}+Aa_ua_v+Ba_v^{\,2}\equiv 0\pmod p au2+Aauav+Bav20(modp)

其中 A , B , p A,B,p A,B,p 都是给定的数。

数据范围: n ≤ 1 0 5 n\le 10^5 n105 3 ≤ p ≤ 1 0 16 3\le p\le10^{16} 3p1016


solution

这好像是二次剩余的模板题啊。

首先可以通过求根公式直接解出那个式子: a u = − A ± A 2 − 4 B 2 a v a_u=\frac{-A\pm\sqrt{A^2-4B}}{2}a_v au=2A±A24B av

那么讨论一下 A 2 − 4 B \sqrt{A^2-4B} A24B 是否有解:

  1. A 2 − 4 B \sqrt{A^2-4B} A24B 无解。那么就相当于统计 a u = a v = 0 a_u=a_v=0 au=av=0 的答案。
  2. A 2 − 4 B \sqrt{A^2-4B} A24B 有解。就按照上式(即 a u = − A ± A 2 − 4 B 2 a v a_u=\frac{-A\pm\sqrt{A^2-4B}}{2}a_v au=2A±A24B av)统计答案。

由于 v v v u u u 的祖先,这就非常好做,用个 map 存一下值即可。

时间复杂度 O ( n ) O(n) O(n)


code

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int gi()  {return Read<int>();}
	inline ll  gl()  {return Read<ll >();}
}
using IO::gi;
using IO::gl;
const int N=1e5+5;
ll A,B,P,inv2,x1,x2,sum,a[N];
inline ll add(ll x,ll y)  {return x+y>=P?x+y-P:x+y;}
inline ll dec(ll x,ll y)  {return x-y< 0?x-y+P:x-y;}
inline ll mul(ll x,ll y)  {return (x*y-(ll)((long double)x/P*y)*P+P)%P;}
inline ll power(ll a,ll b){
	ll ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
inline ll Inv(ll x)  {return power(x,P-2);}
int n,t,first[N],v[N],nxt[N];
inline void edge(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
namespace Cipolla{
	ll I,val;
	struct num{
		ll x,y;
		num(ll x=0,ll y=0):x(x),y(y){}
		inline friend num operator*(const num &a,const num &b){
			return num(add(mul(a.x,b.x),mul(val,mul(a.y,b.y))),add(mul(a.x,b.y),mul(a.y,b.x)));
		}
		inline friend num operator^(num a,ll b){
			num ans(1,0);
			for(;b;b>>=1,a=a*a)  if(b&1)  ans=ans*a;
			return ans;
		}
	};
	inline pll Sqrt(ll n){
		if(!n)  return pll(0,0);
		if(power(n,(P-1)>>1)==P-1)  return pll(-1,-1);
		while(1){
			I=rand(),val=dec(mul(I,I),n);
			if(power(val,(P-1)>>1)==P-1)  break;
		}
		ll x=(num(I,1)^((P+1)>>1)).x;
		return pll(x,P-x);
	}
}
using Cipolla::Sqrt;
int cnt=0;
inline void dfs0(int x){
	if(!a[x])  sum+=cnt,cnt++;
	for(int i=first[x];i;i=nxt[i])  dfs0(v[i]);
	if(!a[x])  cnt--;
}
unordered_map<ll,int>num;
inline void dfs1(int x){
	sum+=num[a[x]];
	ll val1=mul(a[x],x1),val2=mul(a[x],x2);
	(val1==val2)?(++num[val1]):(++num[val1],++num[val2]);
	for(int i=first[x];i;i=nxt[i])  dfs1(v[i]);
	(val1==val2)?(--num[val1]):(--num[val1],--num[val2]);
}
int main(){
	n=gi(),P=gl(),A=gl()%P,B=gl()%P;
	pll delta=Sqrt(dec(mul(A,A),mul(4,B)));
	inv2=Inv(2),x1=mul(add(P-A,delta.fi),inv2),x2=mul(add(P-A,delta.se),inv2);
	for(int i=1;i<=n;++i)  a[i]=gl();
	for(int i=2,fa;i<=n;++i)  fa=gi(),edge(fa,i);
	(delta.fi==-1)?(dfs0(1),1):(dfs1(1),1);
	printf("%lld\n",sum);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值