[考试反思]0212省选模拟23:迷失

这是$NOi$模拟啊。。。

估分50+40+0=90

5分。。。5分。。。

脑子可能落在家,啊不,落在学校了。。。

考后几分钟就达到了估分。混了个六七八名啥的。。。赛后排行榜也一直不太低。。。

最近总是这样,到底为什么啊啊啊。。。

也不知道为什么,在家脑子好像动不起来。。。

$T2$的$40$分暴力是联赛原题,没什么好说的。

$T1$的链和菊花的部分分单反==但凡是花了点时间想的差不多都能想出来。

$T3$读错题了,最后才发现,没有留思考时间。然而感觉思路不是很好想。。。

唯一一个表现比较突出的地方就是$T1$想到了$n \le 5$的做法,如果不丢分的话是个单题最高分。

一塌糊涂。

 

T1:Exceptation

大意:树,边权[0,1]随机,求直径的期望。$n \le 100$

链是$\frac{n}{2}$

菊花是最大值加次大值$\frac{2n-3}{n}$

$n \le 5$发现只有在$n=5$时有一种既不是菊花有不是链的情况,它恰好就是样例。

剩下的不会。

 

T2:Sequence

大意:给定大小写字母串,求子串的本质不同子序列数。在线。$n,q \le 10^6$

本质不同子序列是联赛模拟原题了。$dp[i]$表示以$i$结尾的本质不同子串数。

换个思路,设$dp[i][j]$表示到$i$位置结尾字符为$j$的本质不同子串数。

这个可以转移,而且可以写成矩阵乘法的形式。

矩阵形如:

1  0  0  0  1  0

0  1  0  0  1  0

0  0  1  0  1  0

0  0  0  1  1  0

0  0  0  0  1  0

0  0  0  0  1  1

也就是个单位矩阵,一列变为$1$。

我们的区间询问让我们想到可以用前缀和来维护。然而矩阵乘的前缀和意味着我们需要它的逆元的前缀和。

逆元形如:

1  0  0  0  -1  0

0  1  0  0  -1  0

0  0  1  0  -1  0

0  0  0  1  -1  0

0  0  0  0   1  0

0  0  0  0  -1  1

我们的初始状态是空串,也就是一个行向量$H=[1,0,0,0,...,0]$。

最终我们转移完成后需要求和,也就是要乘上一个列向量$L=[1,1,1,...,1,1,1]^T$。

那么答案就应该是$H \times Inv_{l-1} \times A_r \times L$

此处的$A,Inv$为转移矩阵的前缀积以及逆元。

然而仔细想一想发现,因为矩阵乘法并没有交换律,所以乘的顺序很关键。

$H \times inv_1 \times inv_2 \times ... \times inv_{l-1} \times a_1 \times a_2 \times ... \times a_r \times L$这是不对的。

因为逆元都乘起来再乘原矩阵不一定会得到单位矩阵,没有交换律。下面这个才是对的。

$H \times inv_{l-1} \times inv_{l-2} \times ... \times inv_{2} \times inv_{1} \times a_1 \times a_2 \times ... \times a_r \times L$

这样的话中间相邻的两项才会两两抵消。所以我们在处理逆元的前缀积时实际上要求乘的顺序是倒着乘过去的。

所以接下来我们只要求出$H \prod_{i=l-1}^{1} inv_i$以及$\prod_{i=1}^{r} a_i \times L$即可利用这两个向量,$O(53)$地求出每组询问的答案。

从含义上理解或者从矩阵上来研究,我们发现这个矩阵很特殊。

首先考虑转移矩阵$a$。我们要维护的是$A_i = A_{i-1} \times a_i$。所以考虑一个矩阵乘上$a_i$会发生什么。

发现结果是对于矩阵的每一行,第$s_i$列要加上其余所有列的值。

而最后要乘上$L$则是对每一行求和,所以额外开一个数组维护每行的和即可。

接下来考虑逆元的前缀和。$Inv_i=inv_i \times Inv_{i-1}$。顺序反了过来。

发现逆元矩阵乘上一个矩阵的效果是,除了第$s_i$行以外,其余每行都要对位减去这一行。

减的值都是一样的,不难想到打个标记就好了。我们要算的是$H \times Inv_i$。发现意思也就是保留第一行。

所以上述操作都是$O(53)$的。然后我们要存储的也只是$n$个长为$53$的行列向量。

空间足够时间合法。总复杂度$O(53(n+q))$

 

T3:Counting

大意:$n$点有向图。不断不重复的选出一条边,每次选后记录当前$SCC$数得到$E$数列。求前$i(1 \le i \le n(n-1))$有多少种$E$数列。$n \le 100$

肯定是个$dp$了但是完全看不出怎么做。。。

如果你是神仙,你就能想到,对于每种$E$数列,一定存在一种选边顺序,使得除$1$号点所在的$SCC$外剩下的$SCC$大小均为$1$。

而且,对于每种满足这个条件的选边状态(已连边数,$1$号$SCC$大小,形成$1$号$SCC$过程中$E$变化了几次),它与一个特定的$E$序列一一对应。

所以我们现在的问题是求出选边的方案数。

设$dp[i][j][k]$,对应上面的选边状态。

首先要判断这个状态是否合法,即边数是否过多或过少。

环每在外部闭合一次,那么就一定在生成树基础上多了一条边,所以$i \geq j+k-1$

边数最多的情况是$1$号$SCC$内部已经是完全图且与外部所有点连边,外部所有点形成最长$DAG$。最大边数为$j(j-1)+j(n-j)+C_{n-j}^{2}$

如果这个位置合法那就可以转移了。

$dp[i][j][k]=dp[i-1][j][k]+\sum\limits_{p=1}^{j-1}dp[i-1][p][k-1]$

前缀和优化一下就没了。

$2$个$10^8$数组开得下,$1G$内存就是良心啊。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 998244353
 4 int dp[10001][101][101],sum[10001][101][101];
 5 int mo(int x){return x>=mod?x-mod:x;}
 6 int main(){
 7     dp[0][1][0]=1; int n;cin>>n;
 8     for(int i=1;i<=n;++i)sum[0][i][0]=1;
 9     for(int e=1;e<=n*(n-1);++e)for(int i=1;i<=n;++i)for(int j=0;j<i;++j)
10         dp[e][i][j]=mo(dp[e-1][i][j]+sum[e-1][i-1][j-1]),
11         dp[e][i][j]*=i+j-1<=e&&e<=i*(i-1)+(n-i)*(n-i-1)/2+i*(n-i),
12         sum[e][i][j]=mo(sum[e][i-1][j]+dp[e][i][j]);
13     for(int e=1,a;a=0,e<=n*(n-1);++e){
14         for(int j=0;j<n;++j)a=mo(a+sum[e][n][j]);
15         printf("%d ",a);
16     }
17 }
View Code

 

posted @ 2020-02-13 12:01  DeepinC  阅读(224)  评论(0编辑  收藏  举报