2019牛客暑期多校训练营(第九场)

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$

•代码

 1 #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 }
View Code

D.Knapsack Cryptosystem(折半枚举)

•题意

给出一个长度为n的集合a,

从中选任意个数,使得这些数的和为s

•思路

由于长度最大为36,一共有$2^{36}$种情况

挨个枚举肯定会TLE

利用折半枚举,两个$2^{18}$

•代码

 1 #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 }
View Code

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

•代码

 1 #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 }
View Code
原文地址:https://www.cnblogs.com/MMMinoz/p/11364217.html