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

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

 


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

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

 


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

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

 

posted @ 2019-08-16 16:39  HHHyacinth  阅读(258)  评论(0编辑  收藏  举报