SMSC2021 Day1&Day2

  • Day1
    • T2 CF1552C Maximize the Intersections(推理,数学)
    • T3 表格 table (组合数学,数论,※考虑个体贡献的思想)
  • Day2
    • T1 数字 number (※数论,素数筛)
    • T2 字符串 string (搜索,※剪枝)
    • T3 ※随机树 tree (概率期望,状压DP)
      • 抓住题目的关键
      • 巧妙地设置状态
      • 期望解法1:E=∑贡献次数×贡献总方案数E=\dfrac{\sum贡献次数\times贡献}{总方案数}E=总方案数∑贡献次数×贡献​
      • 期望解法2:E=∑贡献×贡献的概率E=\sum贡献\times贡献的概率E=∑贡献×贡献的概率

Day1

T2 CF1552C Maximize the Intersections(推理,数学)

CF1552C Maximize the Intersections
在一个圆上有 2n2n2n 个不同的点,具有以下性质:无论你如何选择 333 条连接 333 对互不相干的点的弦,在圆内没有一个点严格属于所有 333 条弦。这些点按顺时针顺序被编号为 1,2,…,2n1,2,…,2n1,2,…,2n。
最初,kkk 条和弦连接了 kkk 对点,其方式是这些和弦的所有 2k2k2k 个端点是不同的。
你想画出 n−kn-kn−k 条额外的和弦,连接剩余的$ 2(n-k)$ 个点(每个点必须正好是一个和弦的端点)。
最后,让 xxx 成为所有 nnn 条弦的交叉点的总数。计算如果你以最佳方式选择 n−kn-kn−k 条弦,xxx 能达到的最大值。
请注意,2n2n2n 个点的确切位置并不重要,只要第一段中所述的属性成立即可。

首先考虑 k=0k = 0k=0 的情况:
显然我们希望连一条新的弦的时候,与尽可能多的已有的弦相交,而根据题目所给的性质,我们可以知道连一条新的弦可以与所有已有的弦相交,所以我们构造如下方法:一个圆上有 2n2n2n 个不同的点,对于一个点 iii ,连接它与它的对点形成一条新的弦,它的对点是 i+ni+ni+n。

如此构造可以使得每一条弦都与剩余的弦全部相交,所以答案为 ans⁡=∑i=1ni=n(n−1)2\operatorname{ans} = \sum_{i = 1}^{n} i=\dfrac{n(n-1)}{2}ans=∑i=1n​i=2n(n−1)​

然后考虑 k=1k=1k=1 的情况:
对于如下情况:

我们考虑令新连上的边尽可能多的与已连边相交,并且新连的边尽可能多的互交。为了使新连上的边尽可能多的与已连边相交,我们已连边下方的 aaa 个点全部与上方的点相连,得到 aaa 个交点,同时将这 a+ba+ba+b 个点看作一个新的圆,用 k=0k=0k=0 的情况中的构造方法计算即可,最终答案 ans⁡=a+(a+b)(a+b+1)2\operatorname{ans} = a+\dfrac{(a+b)(a+b+1)}{2}ans=a+2(a+b)(a+b+1)​

最后考虑普遍情况:
其实对于所有情况,我们只要将没有连上边的点全部提出来运用最开始提到的构造方法就可以使得获得的交点最多。对于结论的证明可以考虑用 k=1k=1k=1 的情况类推到所有情况,因为可以将所有已连上的边单独考虑,所以从子问题归纳使得总问题的解法正确性得证。除此以外,考虑新连两条边与已有的一条边的情况,通过画图得知,无论已有的一条边与新连的两条边关系如何,新连的两条边相交总会使得总交点数不减少,所以令任意两条新边相交即为最佳答案。
新连边的交点可以利用公式求得,至于已有边之间的交点与已有边和新连边的交点可以枚举判断求得,对于圆上的点 a,b,c,d(a<b,c<d)a,b,c,d(a<b,c<d)a,b,c,d(a<b,c<d) 所连成的两条弦 (a,b),(c,d)(a,b),(c,d)(a,b),(c,d) ,它们相交的条件是 a<c<b<da<c<b<da<c<b<d 或 c<a<d<bc<a<d<bc<a<d<b,时间复杂度O(n2)O(n^2)O(n2)

#include<iostream>
#include<cstdio>
#include<fstream>using namespace std;
int n,k,T,ans;
int p[5005][2],a[5005],cnt,x,y;
bool vis[5005];int main()
{//  freopen("intersect.in","r",stdin);
//  freopen("intersect.out","w",stdout);scanf("%d",&T);for(int t = 1;t <= T;t ++){scanf("%d%d",&n,&k);ans = 0;if(k == 0){for(int i = 1;i < n;i ++)ans += i;printf("%d\n",ans);continue;}cnt = 0;for(int i = 1;i <= (n+n);i ++ ) vis[i] = 0;for(int i = 1;i <= k;i ++){scanf("%d%d",&x,&y);p[i][0] = min(x,y);p[i][1] = max(x,y);vis[x] = vis[y] = 1;}for(int i = 1;i <= (n+n);i ++)if(vis[i] == 0){cnt ++;a[cnt] = i; }for(int i = 1;i <= (cnt/2);i ++){p[k+i][0] = a[i];p[k+i][1] = a[i+(cnt/2)];}for(int i = 1;i <= k;i ++)for(int j = i+1;j <= n;j ++)if((p[i][0] < p[j][0] && p[j][0] < p[i][1] && p[i][1] < p[j][1]) || (p[j][0] < p[i][0] && p[i][0] < p[j][1] && p[j][1] < p[i][1]))ans ++;for(int i = 1;i < (cnt/2);i ++) ans += i;printf("%d\n",ans);}return 0;
}

T3 表格 table (组合数学,数论,※考虑个体贡献的思想)



最开始考虑根据已给的数据一步一步推到第 111 行,然后在再一步一步推到 (x,y)(x,y)(x,y)
{fi,1=fi+1,1afi,j=fi+1,j−b⋅fi,j−1a(j>1)\begin{cases} &f_{i,1} = \dfrac{f_{i+1,1}}{a}\\[2ex] &f_{i,j} = \dfrac{f_{i+1,j}-b\cdot f_{i,j-1}}{a}(j > 1) \end{cases} ⎩⎪⎪⎨⎪⎪⎧​​fi,1​=afi+1,1​​fi,j​=afi+1,j​−b⋅fi,j−1​​(j>1)​
但是这样做太耗费时间,我们需要一种线性做法。
我们观察题目给出的递推式,尝试做一些简单推算,考虑到在某个位置 (x,y)(x,y)(x,y) 的值 fx,yf_{x,y}fx,y​ 一定受到 fu,v(u<x,v⩽y)f_{u,v}(u<x,v\leqslant y)fu,v​(u<x,v⩽y) 的影响,这时候就该考虑表格中一个值对另一个值的影响了,就要求出它们之间的关系,其关系往往是某一值对另一值有贡献。具体分析如下:考虑已知 fi,jf_{i,j}fi,j​ ,求它对 fx,y(i<x,j⩽y)f_{x,y}(i<x,j\leqslant y)fx,y​(i<x,j⩽y) 的影响(即贡献)。
对于这个问题的一些简单实例,如下图:

我们发现从 fi,jf_{i,j}fi,j​ 推到 f(x,y)f(x,y)f(x,y) 有以下几种情况:

  1. fi,jf_{i,j}fi,j​ 向下 111 次,向右下 222 次 ,贡献 f′=a×b×b×fi,jf'=a\times b\times b\times f_{i,j}f′=a×b×b×fi,j​
  2. fi,jf_{i,j}fi,j​ 向右下 111 次,向下 111 次,向右下 111 次 ,贡献 f′=b×a×b×fi,jf'=b\times a\times b\times f_{i,j}f′=b×a×b×fi,j​
  3. fi,jf_{i,j}fi,j​ 向右下 222 次,向下 111 次 ,贡献 f′=b×b×a×fi,jf'= b\times b\times a\times f_{i,j}f′=b×b×a×fi,j​

从以上计算可得,每一次从 fi,jf_{i,j}fi,j​ 到 fx,yf_{x,y}fx,y​ 的贡献都相同,设为 f′f'f′,设从 (i,j)(i,j)(i,j) 到 (x,y)(x,y)(x,y) 的方案数为 kkk, 那么 fi,jf_{i,j}fi,j​ 的总贡献即为 k×f′k\times f'k×f′。
这样计算贡献的正确性显然,如红色路线
fi+1,j=afi,j+bfi,j−1fi+2,j+1=afi+1,j+1+bfi+1,jfx,y=afi+2,j+2+bfi+2,j+1=ab2fi,j+afi+2,j+2+b3fi,j−1+abfi+1,j+1=ab2fi,j+k\begin{aligned} f_{i+1,j} &= af_{i,j}+bf_{i,j-1}\\ f_{i+2,j+1} &= af_{i+1,j+1}+bf_{i+1,j}\\ f_{x,y}&=af_{i+2,j+2}+bf_{i+2,j+1} \\&=ab^2f_{i,j}+af_{i+2,j+2}+b^3f_{i,j-1}+abf_{i+1,j+1} \\&=ab^2f_{i,j} + k \end{aligned} fi+1,j​fi+2,j+1​fx,y​​=afi,j​+bfi,j−1​=afi+1,j+1​+bfi+1,j​=afi+2,j+2​+bfi+2,j+1​=ab2fi,j​+afi+2,j+2​+b3fi,j−1​+abfi+1,j+1​=ab2fi,j​+k​
由此可知,对于第 ppp 行下方的数 fx,yf_{x,y}fx,y​ ,有
fx,y=∑i=1yC⁡x−py−i×ax−p−y+i×by−i×fp,if_{x,y}=\sum_{i=1}^{y}\operatorname{C}_{x-p}^{y-i}\times a^{x-p-y+i}\times b^{y-i}\times f_{p,i} fx,y​=i=1∑y​Cx−py−i​×ax−p−y+i×by−i×fp,i​
那么有没有从当前行向上推的公式呢?我们继续从简单的实例入手并推导。

我们尝试从当前 ppp 行推出 fx,yf_{x,y}fx,y​,现有以下式子:
fp,y−2=a2fx,y−2fp,y−1=a2fx,y−1+2abfx,y−2fp,y=a2fx,y+2abfx,y−1+b2fx,y−2\begin{aligned} f_{p,y-2}&=a^2f_{x,y-2}\\ f_{p,y-1} &= a^2f_{x,y-1}+2abf_{x,y-2}\\ f_{p,y} &= a^2f_{x,y}+2abf_{x,y-1}+b^2f_{x,y-2} \end{aligned} fp,y−2​fp,y−1​fp,y​​=a2fx,y−2​=a2fx,y−1​+2abfx,y−2​=a2fx,y​+2abfx,y−1​+b2fx,y−2​​
可以推出 fx,y=1a2fp,y−2ba3fp,y−1+3b2a4fp,y−2f_{x,y} = \dfrac{1}{a^2}f_{p,y}-2\dfrac{b}{a^3}f_{p,y-1}+3\dfrac{b^2}{a^4}f_{p,y-2}fx,y​=a21​fp,y​−2a3b​fp,y−1​+3a4b2​fp,y−2​
我们可以发现,当前 ppp 行的元素向上的路径如下图:

当前第 ppp 行的元素第一步必须向上走一步且乘上 1a\dfrac{1}{a}a1​,此后向上走一步且乘上 1a\dfrac{1}{a}a1​或向右走一步且乘上 −ba-\dfrac{b}{a}−ab​,所以我们有:
fx,y=∑i=1yC⁡p−x+y−i−1y−i×(1a)p−x+y−i×(−b)y−i×fp,if_{x,y}=\sum_{i=1}^{y}\operatorname{C}_{p-x+y-i-1}^{y-i}\times (\dfrac{1}{a})^{p-x+y-i}\times(-b)^{y-i}\times f_{p,i} fx,y​=i=1∑y​Cp−x+y−i−1y−i​×(a1​)p−x+y−i×(−b)y−i×fp,i​
综上,对于 ppp 行以下的,我们有:
fx,y=∑i=1yC⁡x−py−i×ax−p−y+i×by−i×fp,if_{x,y}=\sum_{i=1}^{y}\operatorname{C}_{x-p}^{y-i}\times a^{x-p-y+i}\times b^{y-i}\times f_{p,i} fx,y​=i=1∑y​Cx−py−i​×ax−p−y+i×by−i×fp,i​
所以只要我们预处理组合数的运算和幂运算就能够 O(nq)O(nq)O(nq) 快速通过本题。接下来介绍快速计算组合数的一些方法。
首先,线性求逆元在这

inv[i] = (long long)(modn - modn / i) * inv[modn % i] %modn;//求出 i^(-1)

然后由于
C⁡mn=m!n!(m−n)!=m!×(n!)−1×[(m−n)!]−1\operatorname{C}_{m}^{n}=\dfrac{m!}{n!(m-n)!}=m!\times(n!)^{-1}\times[(m-n)!]^{-1} Cmn​=n!(m−n)!m!​=m!×(n!)−1×[(m−n)!]−1
所以我们要预处理阶乘逆元和阶乘

prod[i] = prod[i-1] * i % modn;//阶乘i!
invpord[i] = invprod[i-1] * inv[i] % modn;//逆元阶乘 (i!)^(-1)
long long C(int n,int m)
{if(n > m) return 0;return prod[m] * invpord[n] * invprod[m-n] % modn;
}

由此可以 O(1)O(1)O(1) 计算组合数。


Day2

T1 数字 number (※数论,素数筛)

一个数如果是另一个整数的平方,那么我们就称这个数为完全平方数。
小 A 希望用任意个不大于 nnn 的不同正整数相乘得到完全平方数,并且小 A 希望这个平方数越大越好。
请你帮助小 A 找到满足题意的最大的完全平方数。

刚开始很难发现思路,题目的要求使得我们要构造答案。首先对于“最大”这个限制,我们考虑将尽可能多的正整数相乘。对于“完全平方数”这一限制,我们考虑如果一个数 xxx 是完全平方数,那么一定存在正整数 aaa 使得 x=a2x = a^2x=a2。而对于正整数 aaa ,由整数的唯一分解定理可得 a=p1k1×p2k2×p3k3⋯×pnkna=p_1^{k_1}\times p_2^{k_2}\times p_3^{k_3}\cdots \times p_n^{k_n}a=p1k1​​×p2k2​​×p3k3​​⋯×pnkn​​,其中 pip_ipi​ 均为素数。由此推得 x=p12k1×p22k2×p32k3⋯×pn2knx=p_1^{2k_1}\times p_2^{2k_2}\times p_3^{2k_3}\cdots \times p_n^{2k_n}x=p12k1​​×p22k2​​×p32k3​​⋯×pn2kn​​,所以完全平方数可以通过任意素数的偶次幂相乘而得。综上所述,我们可以想到,通过使不大于 nnn 的所有正整数中包含的素数的最大偶次幂相乘构造最大的完全平方数。一种朴素的算法是从 111 枚举到 nnn ,然后将每个数都质因数分解,统计各个质数出现的次数,最后设第 iii 个质数 pip_ipi​ 出现次数为 kik_iki​ ,那么答案为 Ans⁡=∏i=1mpi(ki−[kimod2=1])\operatorname{Ans} = \prod_{i=1}^{m}p_i^{(k_i-[k_i \bmod 2 = 1])}Ans=i=1∏m​pi(ki​−[ki​mod2=1])​
事实上,有以下定理:对于 n!n!n! 它可以分解出质数 ppp 的个数为 ∑i=1∞⌊npi⌋\sum_{i=1}^{\infin}\lfloor{\dfrac{n}{p^i}}\rfloori=1∑∞​⌊pin​⌋
如此,我们就可以降低朴素分解质因数的高复杂度。此外,我们更可以运用欧拉线性筛等素数筛法提高筛素数的效率。

#include<iostream>
#include<cstdio>
#include<fstream>
#include<cmath>using namespace std;
const unsigned long long modn = 100000007;
int n ,maxprime;
bool bo[5000005];
int cnt[5000005],pri[5000005],cnter,tmp,tmp1;
unsigned long long ans;int main()
{scanf("%d",&n);bo[1] = 1;bo[2] = 0;for(int i = 2;i <= n;i ++)if(bo[i] == 0){for(int j = i+i;j <= n;j += i)bo[j] = 1;cnter ++;pri[cnter] = i;
//          cnt[i] ++;}for(int i = 1;i <= cnter;i ++){tmp1 = n;while(tmp1 != 0){cnt[pri[i]] += tmp1/pri[i];tmp1 /= pri[i];}}ans = 1;for(int i = 1;i <= cnter;i ++){if(cnt[pri[i]]%2 == 1) cnt[pri[i]] --;for(int j = 1;j <= cnt[pri[i]];j ++)ans = (ans*pri[i])%modn;}printf("%llu",ans%modn);return 0;
}

T2 字符串 string (搜索,※剪枝)

对于字符串 sss ,若它的长度为 nnn ,最小循环节长度为 mmm ,那么我们定义它的价值为 n2m\dfrac{n^2}{m}mn2​。(对字符串 sss ,称 ttt 为其循环节,当且仅当 s=tttt⋯tts=tttt\cdots tts=tttt⋯tt。最小循环节为 ∣t∣|t|∣t∣ 最小的一个)
给定一个只包含 a,b,c的字符串,求它的价值最大的非空子序列的价值。

我们可以直接 O(2n)O(2^n)O(2n) 枚举循环节(循环节一定是一个子序列),然后每次 O(n)O(n)O(n) 的扫描原串进行检验。
复杂度 O(n⋅2n)O(n\cdot 2^n)O(n⋅2n)。
我们考虑长度为 nnn 的子序列中,由鸽巢原理得某一字母出现的次数 k1⩾n3k_1\geqslant\dfrac{n}{3}k1​⩾3n​,那么由单一字母组成的序列价值 val⁡=(n3)2=n29\operatorname{val} = (\dfrac{n}{3})^2=\dfrac{n^2}{9}val=(3n​)2=9n2​,对于相同长度的子序列而言,显然当循环节的长度 m⩽9m\leqslant 9m⩽9 才能保证其价值不低于全部由单一字母组成的子序列,由此减少了我们搜索的范围,考虑枚举所有长度不大于 999 的序列,将它们作为循环节套入原串中计算并比较得出答案,时间复杂度 O((∑i=183i)n)O((\sum_{i=1}^{8}3^i)n)O((∑i=18​3i)n)
为了继续改进复杂度,我们需要找出一个更好的剪枝方案,所以我们继续发掘一些隐含的性质。
考虑现有一个价值最大的子序列 sss ,其循环节为 ttt ,设循环节 ttt 在子序列 sss 中出现的次数为 k1k_1k1​,设 ttt 中又有一子序列 s′s's′ 在单一循环节中出现次数为 k2k_2k2​ ,将 s′s's′ 全部抽出,组成的子序列的价值为 val′⁡=k12k22∣s′∣\operatorname{val'}=k_1^2k_2^2|s'|val′=k12​k22​∣s′∣,子序列 sss 的价值 val⁡=n2m=k12∣t∣\operatorname{val} =\dfrac{n^2}{m} =k_1^2|t|val=mn2​=k12​∣t∣。因为子序列的价值最大,那么 val⁡⩾val⁡′\operatorname{val} \geqslant \operatorname{val}'val⩾val′ 即 ∣t∣⩾k22∣s′∣|t|\geqslant k_2^2|s'|∣t∣⩾k22​∣s′∣,设 s′s's′ 为单一字母,根据鸽巢原理,我们有 m⩾(m3)2m\geqslant (\dfrac{m}{3})^2m⩾(3m​)2,可以推得 m⩽6m\leqslant 6m⩽6,所以循环节的长度不应大于 666。
最后还有一个更强的性质:如果某种字母在循环节中出现了 333 次或以上,那么我们直接取出所有这种字母是更优的。假设答案子序列长度为 nnn,循环节长度为 mmm 且 a出现了 333 次,那么我们设循环节个数 k=nmk = \dfrac{n}{m}k=mn​,那么答案是 k2mk^2mk2m ;但我们将所有的 aaa 提取出来,那么循环节出现次数至少为 3k3k3k,长度变为 111,则新答案为 9k29k^29k2。由于 m⩽9m\leqslant 9m⩽9,因此新答案更优。因此只要考虑每个字母都 ⩽2\leqslant 2⩽2 次的循环节。这样的循环节长度至多为 666 ,且只有两百多种。确定循环节之后,扫一遍原串就能求出循环节的最大出现次数。时间复杂度 O(300×n)O(300\times n)O(300×n) 。

#include<iostream>
#include<cstdio>
#include<fstream>
#include<string>
#include<cstring>using namespace std;
typedef long long ll;
int T,n;
int cnt,strsiz,tmp;
ll len[1850],patsiz,ans,val;
int vis[5];
char ch[10];
string pat[1850],str,tmpstr;void dfs(int now,int sum)
{if(now > sum){cnt ++;for(int i = 1;i <= sum;i ++) pat[cnt] += ch[i];if(sum > 1){for(int i = 1;i < cnt;i ++){tmpstr.clear();patsiz = pat[i].size();if(sum%patsiz > 0 ) continue;if(sum == patsiz) break;for(int j = 1;j <= (sum/patsiz);j ++)tmpstr += pat[i];if(tmpstr == pat[cnt]){pat[cnt].clear();cnt --;break;}}}return ;}for(int i = 1;i <= 3;i ++)if(vis[i] < 2){ch[now] = 'a'+i-1;vis[i] ++;dfs(now+1,sum);vis[i] --;}return ;
}int main()
{//  freopen("string.in","r",stdin);for(int i = 1;i <= 6;i ++)dfs(1,i);scanf("%d",&T);for(int tt = 1;tt <= T;tt ++){scanf("%d",&n);cin >> str;strsiz = str.size();for(int i = 1;i <= cnt;i ++){patsiz = pat[i].size();tmpstr = pat[i];tmp = 0;for(int j = 0;j < strsiz;j ++){if(tmpstr[tmp] == str[j]){tmp ++;if(tmp == patsiz){len[i] += patsiz;tmp = 0;}}}}ans = 0; for(int i = 1;i <= cnt;i ++){patsiz = pat[i].size();val = len[i]/patsiz*len[i];ans = max(ans,val);len[i]  = 0;}printf("%lld\n",ans);}return 0;
}

T3 ※随机树 tree (概率期望,状压DP)

#6495. 「雅礼集训 2018 Day1」树

通过本题学会一种新的状态表示方式。

抓住题目的关键

在本题中,由于答案求树的深度的期望,我们不关心具体结点的具体位置,我们只关注随机生成的一种树的概率和其深度。随机生成的一种树的过程中,我们考虑每加入一个新的点,可以形成多少棵不同的树,并且要知道形成的树的深度是多少。每个点向之前的点连边的概率相等,并且我们只关心之前每个点的深度。

巧妙地设置状态

我们可以考虑把之前每个点的深度排序后作为状态进行状压 DP 。由于深度排序后必然是连续的,差分后可以得到一个 010101 串,于是我们巧妙地表达了状态。例如:一个树如果在第 1,2,3,41,2,3,41,2,3,4 层分别有结点个数 1,3,2,41,3,2,41,3,2,4 ,则状态可以表示为 122233444412223344441222334444 ,差分后即 110010100011001010001100101000。

如此设置状态以后,我们可以得知一棵树的深度(即 111 的个数),可以得知每一层的结点数,所以对于状态的转移,在某一层加入一个新点就是在某一层的状态中加 000 或者在新的一层即串的末尾加 111。

期望解法1:E=∑贡献次数×贡献总方案数E=\dfrac{\sum贡献次数\times贡献}{总方案数}E=总方案数∑贡献次数×贡献​

我们设 f(S)f(S)f(S) 表示在 SSS 状态下生成的树的方案数,已有状态集合为 A={Spre}A=\{S_{pre}\}A={Spre​} ,那么在考虑加入一个点 iii 的时候,f(S)=∑Sj∈Af(Sj)×kf(S) = \sum_{S_{j}\in A} f(S_{j})\times kf(S)=∑Sj​∈A​f(Sj​)×k ,其中 kkk 表示 iii 在 SjS_{j}Sj​ 中可以选择的父亲的方案数。

我们设终态集合为 {Se}\{S_e\}{Se​},设某一状态的树高为 H(S)H(S)H(S) ,则最终答案为 E=∑Si∈{Se}f(Si)H(Si)∑Si∈{Se}f(Si)E=\dfrac{\sum_{S_i\in\{S_e\}} f(S_i)H(S_i)}{\sum_{S_i\in\{S_e\}}{f(S_i)}}E=∑Si​∈{Se​}​f(Si​)∑Si​∈{Se​}​f(Si​)H(Si​)​

期望解法2:E=∑贡献×贡献的概率E=\sum贡献\times贡献的概率E=∑贡献×贡献的概率

我们设 P(S)P(S)P(S) 表示达到状态 SSS 的概率,类似地,我们可以得到加入一个新点 iii 的方程 P(S)=∑Sj∈AP(Sj)×ki−1P(S)=\sum_{S_j\in A}P(S_j)\times \dfrac{k}{i-1}P(S)=∑Sj​∈A​P(Sj​)×i−1k​,所以最终答案为 E=∑Si∈{Se}P(Si)H(Si)E=\sum_{S_i\in\{S_e\}} P(S_i)H(S_i)E=∑Si​∈{Se​}​P(Si​)H(Si​)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll n, p;
ll Hsum,tot,sum[30],dep,pos[30],tmpstat,g[17000000];
ll beg,end,nxt_stat,num;
ll ans1,ans2,inv[200];
double tmpans,prob[17000000],EH;
bool fir = 1;inline ll qmult(ll x,ll y)
{ll z = (long double)x/p*y;ll res = (ull)x*y-(ull)z*p;return (res+p)%p;
}int init_stat(int len)
{if(len == 1) return 1;return ((1<<(len-1))+(1<<(len-2)));
}int end_stat(int len)
{return (1<<len)-1;
}ll cnt1(int x)
{ll tmp = 0 ;for(;x;){tmp ++;x=x&(x-1);}return tmp;
}void cnt2(int x,int len)
{dep = 0;int tmp = 0;for(int i = 0;i < len;i ++){tmp ++;if(x&(1<<i)){dep ++;sum[dep] = tmp;pos[dep] = i;tmp = 0;}}return ;
}ll rd(double x)
{return (x > 0.0) ? (x + 0.5):(x - 0.5);
}int main()
{scanf("%lld%lld",&n,&p);inv[0] = 1;inv[1] = 1;for(int i = 2;i <= 100;i ++) inv[i] = (qmult((p - p/i),inv[p%i])+p)%p;prob[1] = 1.00;g[1] = 1.00;for(int i = 1;i < n;i ++){beg = init_stat(i);end = end_stat(i);for(int j = beg;j <= end;j ++){fir = 1;cnt2(j,i);tmpstat = j;for(int k = 1;k <= dep;k ++){if(k == 1) nxt_stat = (j<<1)+1;else{if(k == 2) nxt_stat = j << 1;else nxt_stat = (tmpstat<<1)|(((1<<pos[k-1])-1)&j);tmpstat = tmpstat&(tmpstat-1);}
//              f[nxt_stat] += f[j]*sum[k];prob[nxt_stat] += prob[j]*((double(sum[k]))/(double(i)));g[nxt_stat] = (g[nxt_stat]%p + g[j]*inv[i]%p*sum[k]%p)%p; }}}beg = init_stat(n);end = end_stat(n);for(int i = beg;i <= end;i ++){num = cnt1(i);EH += prob[i]*(double(num));Hsum = (Hsum%p + g[i]*num%p)%p;}ans1 = rd(EH);ans2 = Hsum;printf("%lld",ans1);printf("\n%lld",(ans2+p)%p);return 0;
}

SMSC2021 Day1Day2 部分题解相关推荐

  1. SMSC2021 Day3Day4 部分题解

    SMSC2021 Day3&Day4 Day3 T1 传送 portal (※建图,最短路) T2 图计数 graph (计数DP,组合数学,※状态优化设计) 发现性质 设计状态与状态转移 优 ...

  2. SMSC2021 Day5Day6 部分题解

    SMSC2021 Day5&Day6 Day5 T1 矩阵 matrix (※差分) Day6 T1 旅行 travel (※差分,树上二分与倍增) T2 串串串 string (二分答案,状 ...

  3. SMSC2021 Day11Day12 部分题解

    SMSC2021 Day11&Day12 Day11 数对 pair (数论,同余,计数) 序列 seqn (计数,动态规划,拆分贡献思想) Day12 构造图 graph (数论,约数,质数 ...

  4. SMSC2021 Day7Day8 部分题解

    SMSC2021 Day7&Day8 Day 7 T3 纸带染色 color (区间 DP ,※子问题化分治) Day 8 T2 购票 ticket (最短路,※点权转边权,虚拟源点技巧) D ...

  5. SMSC2021 Day9Day10 部分题解

    SMSC Day9&Day10 Day 9 T3 三染色 tres (构造,二分图判定,动态规划解决判定性问题) Day 10 T3 中位数 mid (性质发掘) T4 接水果 nel (DP ...

  6. [JS][dfs]题解 | #迷宫问题#

    题解 | #迷宫问题# 题目链接 迷宫问题 题目描述 定义一个二维数组 N*M ,如 5 × 5 数组下所示: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 1, 1 ...

  7. [JS][dp]题解 | #打家劫舍(一)#

    题解 | #打家劫舍(一)# 题目链接 打家劫舍(一) 题目描述 描述 你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家, ...

  8. [JS]题解 | #魔法数字#

    题解 | #魔法数字# 题目链接 魔法数字 题目描述 牛妹给牛牛写了一个数字n,然后又给自己写了一个数字m,她希望牛牛能执行最少的操作将他的数字转化成自己的. 操作共有三种,如下: 在当前数字的基础上 ...

  9. [JS]题解 | #岛屿数量#

    题解 | #岛屿数量# 题目链接 岛屿数量 题目描述 时间限制:1秒 空间限制:256M 描述 给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛.我们只考虑上下左右 ...

最新文章

  1. jenkins配置从节点
  2. spss分析qpcr数据_SPSS 数据分析,掌握这 6 大模块就够了!
  3. 白话数字签名(番外篇)----签名EXE文件(下)
  4. C++类型转换: static_cast const_cast reinterpret_cast dynamic_cast
  5. Flink 在小红书推荐系统中的应用
  6. myeclipse部署ssh项目工程
  7. VS2008 vs2010中JQUERY智能提醒
  8. 这届 360 公关不行
  9. Android 返回键的处理
  10. C#高级编程 第十五章 反射
  11. [转载] Python Pandas 转换unix时间戳
  12. luogu1984 烧水问题 (找规律)
  13. Android手机使用Windows应用,微软宣布在你的手机应用上运行安卓APP功能向Windows 10稳定版提供...
  14. MyBatis-Plus学习
  15. 汇编语言(王爽)第七章与实验6
  16. php函数讲解,php函数进阶讲解
  17. Timesten Classic 18.1 建立缓存组
  18. 为什么打印机打印照片模糊_我用打印机打印照片为什么不清楚?应该怎样调 – 手机爱问...
  19. matlab指派问题求法,matlab指派问题
  20. 基于WDF的驱动开发

热门文章

  1. 第一章:腾讯云轻量应用服务器建站流程(前端)
  2. 雷神的微软平台安全宝典---第二章 RSA和AES
  3. 快速导入源代码到word 软件著作权源代码需要
  4. 第一章 完美掌握把“No”变成“Yes”的技巧
  5. 使用VSTS的Git进行版本控制(二)——提交保存工作
  6. 社交App都有哪些类别
  7. Java使用pb【protobuf】压缩解决二维码内容过多导致二维码太密的问题
  8. 笔记本/台式机改造成centos7服务器 + 宝塔面板服务器磁盘挂载(亲测,能打版)
  9. 简单数据库实现——Part3 - 一个内存存储-只可追加-单数据表的数据库
  10. InnoDB学习笔记一引擎架构及特性