2019牛客暑期多校训练营(第九场)
B.Quadratic equation(二次剩余)
•题意
给你 b,c 值,求解 x,y 使得其满足,其中 p=109+7:
1.$0\leq x\leq y <p$
2.$x+y\equiv b\ (mod\ p)$
3.$xy\equiv c\ (mod\ p)$
•题解
易得 $(x-y)^{2} \equiv (x+y)^{2}-4xy \ (mod\ p)_{\cdots\cdots\cdots} (1)$;
将条件 1,2 带入上式得:
$(x-y)^{2} \equiv b^{2}-4c \ (mod\ p)_{\cdots\cdots\cdots} (2)$;
首先,判断 (2) 式是否由解,即判断 $b^{2}-4c$ 是否为模 p 得二次剩余;
令 $d=b^{2}-4c$;
①$d^{\frac{p-1}{2}}\equiv 1 \ (mod\ p)$:
那么,易得 $(x-y)^{2} \equiv d^{\frac{p+1}{2}}\ (mod\ p)_{\cdots\cdots\cdots} (3)$;($d \equiv d\cdot d^{\frac{p-1}{2}}\ (mod\ p)$)
同余式两端同时开方得:$(x-y) \equiv d^{\frac{p+1}{4}}\ (mod\ p)_{\cdots\cdots\cdots} (4)$;
并且,易得 (p+1)%4 == 0;
所以,$d^{\frac{p+1}{4}}$ 是 (4) 式得整数解;
由条件 2 和式 (4) 可得:
$2x \equiv b+d^{\frac{p+1}{4}}\ (mod\ p)_{\cdots\cdots\cdots} (5)$
又因为 GCD(2,p) = 1,所以,式(5)可化为:
$x \equiv (b+d^{\frac{p+1}{4}})\cdot inv_{2} \ (mod\ p)_{\cdots\cdots\cdots} (6)$;
通过式 (6) 顺利求出 x;
由条件 1 可得 x+y = b or b+p;
那么,y 也就求出来了;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 5 ll p=1e9+7; 6 ll b,c; 7 8 ll qPow(ll a,ll b) 9 { 10 ll ans=1; 11 while(b) 12 { 13 if(b&1) 14 ans=ans*a%p; 15 a=a*a%p; 16 b >>= 1; 17 } 18 return ans; 19 } 20 void Solve() 21 { 22 ll d=b*b-4*c; 23 d=(d+p)%p;///保证d为正,不影响答案的正确性 24 25 if(d == 0)///特判d为0的情况 26 { 27 printf("%lld %lld\n",b/2,b/2); 28 return ; 29 } 30 if(qPow(d,(p-1)/2) != 1)///判断d是否为模p的二次剩余 31 { 32 puts("-1 -1"); 33 return ; 34 } 35 36 ll x=(b+qPow(d,(p+1)/4))%p*qPow(2,p-2)%p; 37 ll y=b-x; 38 y=(y+p)%p; 39 40 printf("%lld %lld\n",min(x,y),max(x,y)); 41 } 42 int main() 43 { 44 int T; 45 scanf("%d",&T); 46 while(T--) 47 { 48 scanf("%lld%lld",&b,&c); 49 Solve(); 50 } 51 return 0; 52 }
D.Knapsack Cryptosystem(折半搜索)
•题意
给你 n 个数和 s(n ≤ 36);
求出由这 n 个数的哪些数相加可以得到 s;
•题解
将这 n 个数拆成两部分,前 $\frac{n}{2}$ 个数和后 $n-\frac{n}{2}$ 个数;
你会发下,单个部分的总个数最大为 13 个,那么,便可暴力求解这些数的全部组合结果;
并判断单个部分是否可以组成 s,如果组不成,判断这两部分是否可以组成 s;
时间复杂度 $O(2^{\frac{n}{2}})$,时限 2s;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 5 int n; 6 ll s; 7 ll a[40]; 8 map<ll ,ll >mp[2]; 9 10 void DFS(int cur,ll cnt,int k) 11 { 12 if((k == 0 && cur > n/2) || (k == 1 && cur > n)) 13 return ; 14 15 mp[k][cnt+a[cur]]=mp[k][cnt]+(1ll<<cur); 16 DFS(cur+1,cnt+a[cur],k); 17 DFS(cur+1,cnt,k); 18 } 19 void Solve() 20 { 21 mp[0][0]=0; 22 DFS(1,0,0); 23 24 mp[1][0]=0; 25 DFS(n/2+1,0,1); 26 27 for(auto it=mp[0].begin();it != mp[0].end();++it) 28 { 29 ll fir=it->first; 30 if(mp[1].count(s-fir)) 31 { 32 ll sec=it->second; 33 for(int i=1;i <= n;++i) 34 { 35 if(sec&(1ll<<i)) 36 printf("1"); 37 else 38 printf("0"); 39 40 if(i == n/2) 41 sec=mp[1][s-fir]; 42 43 } 44 return ; 45 } 46 } 47 } 48 int main() 49 { 50 scanf("%d%lld",&n,&s); 51 for(int i=1;i <= n;++i) 52 scanf("%lld",a+i); 53 54 Solve(); 55 56 return 0; 57 }
E.All men are brothers(并查集+数学)
•题意
初始,有 n 个人,编号为 1~n;
这 n 个人构成 n 个朋友圈(即每个人独自构成一个朋友圈);
有 m 个操作,每次操作给出两个数 x,y,指的是将 x 及其朋友圈与 y 及其朋友圈合并;
每次操作后,让你选出 4 个人,满足选出来的这 4 个人相互都不在同一个朋友圈中;
问有多少种选法;
•题解
假设有 n 个人,当前形成的状态为:
有 A,B,C,D,E 共 5 个朋友圈,分别有 a,b,c,d,e 人,a+b+c+d+e = n;
令 $u=a^{2}+b^{2}+c^{2}+d^{2}+e^{2}$;
从当前状态抽取 4 个满足条件的选法共有 ans 种:
$ans=abcd+abce+abde+acde+bcde$;
如果当前合并的朋友圈为 A,B;
那么合并后,有 AB,C,D,E 共 4 个朋友圈,分别有 a+b,c,d,e 人;
从当前状态抽取 4 个满足条件的选法共有 ans' 种:
$ans'=(a+b)cde$;
如何根据 ans 求出 ans' 呢?
令 $tmp=ans-ans'=abcd+abce+abde+acde$;
经过推导可得:$tmp=ab(cd+ce+de)=ab\cdot \frac{(c+d+e)^{2}-(c^{2}+d^{2}+e^{2})}{2}$;
易得 $c+d+e=n-a-b\ ,\ c^{2}+d^{2}+e^{2}=u-a^{2}-b^{2}$;
那么,便很容易算出 tmp 的值;
$ans'=ans-tmp$;
求出 ans' 后,更新 $u=(a+b)^{2}+c^{2}+d^{2}+e^{2}$;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define ul unsigned long long 5 const int maxn=1e5+50; 6 7 int n,m; 8 int fa[maxn]; 9 ll sum[maxn]; 10 int Find(int x) 11 { 12 return x == fa[x] ? x:fa[x]=Find(fa[x]); 13 } 14 ul C(ul n,ul k)///记得用unsigned long long,当心越界 15 { 16 k=min(k,n-k); 17 ul a=1; 18 ul b=1; 19 for(int i=1;i <= k;++i) 20 { 21 a *= (n-i+1); 22 b *= i; 23 if(a%b == 0) 24 a /= b,b=1; 25 } 26 return a/b; 27 } 28 void Init() 29 { 30 for(int i=1;i < maxn;++i) 31 { 32 fa[i]=i; 33 sum[i]=1; 34 } 35 } 36 int main() 37 { 38 Init(); 39 40 scanf("%d%d",&n,&m); 41 42 ll ans=C(n,4); 43 printf("%lld\n",ans); 44 45 ll u=n;///初始,u=1+1+1....=n 46 for(int i=1;i <= m;++i) 47 { 48 int x,y; 49 scanf("%d%d",&x,&y); 50 51 x=Find(x); 52 y=Find(y); 53 54 if(x != y) 55 { 56 ll a=sum[x];///x所在的朋友圈的总人数 57 ll b=sum[y];///y所在的朋友圈的总人数 58 ll v=n-a-b; 59 u -= a*a+b*b; 60 ans -= a*b*(v*v-u)/2; 61 62 /** 63 更新u,fa[x],sum[y] 64 */ 65 u += (a+b)*(a+b); 66 fa[x]=y; 67 sum[y] += sum[x]; 68 } 69 70 printf("%lld\n",ans); 71 } 72 return 0; 73 }