B.Quadratic equation(二次剩余)
•题意
给定$p=1000000007$
有两个数x,y,其中$xleqslant y leqslant p$
$x + y equiv b(mod p)$
$x imes y equiv c (mod p)$求 x,y的值
•先行知识
二次剩余
[1]二次剩余参考资料1
[2]二次剩余参考资料2
•思路
知道 $x+y$ 和 $ x imes y $
那计算x,y 自然是$ ( x-y )^{^{2}} = ( x+y )^{^{2}} - 4xy = b^{2}-4c $
设$b^{2}-4c = d$,利用二次剩余求解
egin{cases}
1,&d^{frac{p-1}{2}} ext{在模$p$意义下是二次剩余}\
-1,&d^{frac{p-1}{2}} ext{在模$p$意义下是非二次剩余}\
0,&d^{frac{p-1}{2}}equiv0pmod p
end{cases}有解时:
①当 $d=0$ 时,两个解相等,都为$frac{b}{2}$
②当d是 $mod p$ 的平方剩余时有$d^{frac{p-1}{2}} equiv 1 (mod p)$
设$p-1=2^{t} imes s,(2∤s)$,显然$t=1$
所以$ ( x-y )=d^{frac{s+1}{2}}$是$( x-y )$的一个解
所以$x-y=d^{frac{s+1}{2}}= d^{frac{p+1}{4}}$
由于$p=1000000007$,所以$ (p+1) mod 4 =0$可行
因为$x-y= d^{frac{p+1}{4}}$,所以$(x - y)mod p equiv d^{frac{p+1}{4}} mod p$ 设为 a
然后再利用 $x + y equiv b(mod p)$
$left { _{x + y equiv b(mod p)}^{x - y equiv a(mod p)} ight.$
得到x,y
注意 此时是在模p的情况下,
在除以2的时候记得除法取模
$frac{m}{n} mod p = mn^{p-2} mod p$
•代码
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define p 1000000007 5 ll qpow(ll a,ll b) 6 { 7 ll res=1; 8 while(b) 9 { 10 if(b&1) 11 res=res*a%p; 12 a=a*a%p; 13 b>>=1; 14 } 15 return res; 16 } 17 int main() 18 { 19 // freopen("C:/Users/14685/Desktop/stdin&&stdout/contest","r",stdin); 20 int t; 21 cin>>t; 22 while(t--) 23 { 24 ll b,c; 25 cin>>b>>c; 26 ll d=(b*b-4*c+p)%p; 27 if(d==0) 28 cout<<b/2<<' '<<b/2<<endl; 29 else 30 { 31 ll yes=qpow(d,(p-1)/2); 32 if(yes==1)//有解 33 { 34 ll a=(qpow(d,(p+1)/4))%p; 35 //除法取模 36 ll x=(b+a)%p*qpow(2,p-2)%p%p; 37 ll y=((b-a)+p)%p*qpow(2,p-2)%p%p; 38 if(x>y) 39 swap(x,y); 40 cout<<x<<' '<<y<<endl; 41 } 42 else 43 cout<<"-1"<<' '<<"-1"<<endl; 44 } 45 } 46 }
D.Knapsack Cryptosystem(折半枚举)
•题意
给出一个长度为n的集合a,
从中选任意个数,使得这些数的和为s
•思路
由于长度最大为36,一共有$2^{36}$种情况
挨个枚举肯定会TLE
利用折半枚举,两个$2^{18}$
•代码
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 map<ll,int> mp; 5 ll a[40]; 6 int main() 7 { 8 int n; 9 ll s; 10 cin>>n>>s; 11 int m=n/2; 12 for(int i=0;i<n;i++) 13 cin>>a[i]; 14 15 for(int i=0;i<(1<<m);i++) 16 { 17 ll sum1=0; 18 for(int j=0;j<m;j++) 19 if(i&(1<<j))//1代表选,0代表不选 20 sum1+=a[j]; 21 mp[sum1]=i+1; 22 } 23 24 for(int i=0;i<(1<<(n-m));i++) 25 { 26 ll sum2=0; 27 for(int j=0;j<n-m;j++) 28 if(i&(1<<j)) 29 sum2+=a[j+m]; 30 int cnt=mp[s-sum2]; 31 if(cnt) 32 { 33 cnt--; 34 for(int j=0;j<m;j++) 35 cout<<((cnt&(1<<j))?1:0); 36 for(int j=0;j<n-m;j++) 37 cout<<((i&(1<<j))?1:0); 38 39 return 0; 40 } 41 } 42 }
E.All men are brothers(并查集+思维)
•题意
一开始有n个人不认识。有m个回合,他们中的两个轮流交朋友。
朋友关系是相互的、可传递的。
如果A是B的朋友,那么B也是A的朋友。
例如,如果A是B的朋友,B是C的朋友,那么A和C是朋友。
在开始和每个回合之后,请计算从中选择四个人的方法的数量,这样这四个人中的任何两个都不是朋友。
•思路
假设现在有5个朋友圈A,B,C,D,E
5个朋友圈里的人数分别为a,b,c,d,e
那么现在的方式有$(abcd+abce+abde+acbd+acde)$种
此时令A B做朋友,也就是A B朋友圈合并
那现在的方式有$((a+b)cde)$,也就是$(acde+bcde)$种
减少了$(abcd+abce+abde)$,即$ab(cd+ce+de)$种
因为$cd+ce+de=((c+d+e)^{2}-(c^{2}+d^{2}+e^{2}))/2$
用一个$n$代表总人数,$S$记录所有集合的平和方
可以把$(c+d+e)$看做$(n-a-b)$,把$(c^{2}+d^{2}+e^{2})$看做$(S-a^{2}+b^{2})$
我们可以利用并查集合并、查找集合,
由于每次时两个集合合并,a,b不合并的平方和为$a^{2}+b^{2}$
合并后的平方和为$(a+b)^{2}=a^{2}+b^{2}+2ab$
所以每次合并后$s+=2ab$
注意 当所有集合都不合并时答案$ extrm{C}_{n}^{4}$可能会long long溢出,
所以使用unsigned long long
•代码
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll unsigned long long 4 int const maxn=2e5+5; 5 int pre[maxn]; 6 ll num[maxn]; 7 8 int Find(int x) 9 { 10 int r=x; 11 while(r!=pre[r]) 12 r=pre[r]; 13 14 int i=x,j; 15 while(pre[i]!=r) 16 { 17 j=pre[i]; 18 pre[i]=r; 19 i=j; 20 } 21 return r; 22 } 23 24 void mix(int x,int y) 25 { 26 int fx=Find(x),fy=Find(y); 27 if(fx!=fy) 28 { 29 pre[fy]=fx; 30 num[fx]+=num[fy]; 31 num[fy]=0; 32 } 33 } 34 35 int main() 36 { 37 ll n,m; 38 scanf("%lld%lld",&n,&m); 39 for(int i=1;i<=n;i++) 40 { 41 pre[i]=i; 42 num[i]=1; 43 } 44 45 ll ans=n*(n-1)/2*(n-2)/3*(n-3)/4; 46 ll s=n; 47 printf("%lld ",ans); 48 int a,b; 49 for(int i=1;i<=m;i++) 50 { 51 scanf("%d%d",&a,&b); 52 a=Find(a),b=Find(b); 53 if(ans==0) 54 { 55 puts("0"); 56 continue; 57 } 58 if(a==b) 59 { 60 printf("%lld ",ans); 61 continue; 62 } 63 ans-=num[a]*num[b]*((n-num[a]-num[b])*(n-num[a]-num[b])-(s-num[a]*num[a]-num[b]*num[b]))/2; 64 s+=2*num[a]*num[b]; 65 printf("%lld ",ans); 66 mix(a,b); 67 } 68 return 0; 69 }