题意:
羽月最近发现,她发动能力的过程是这样的:
构建一个
V
V
V 个点的有向图
G
G
G,初始为没有任何边,接下来羽月在脑中构建出一个长度为
E
E
E 的边的序列,序列中元素两两不同,然后羽月将这些边依次加入图中,每次加入之后计算当前图的强连通分量个数并记下来,最后得到一个长度为
E
E
E 的序列,这个序列就是能力的效果。
注意到,可能存在边的序列不同而能力效果相同的情况,所以羽月想请你帮她计算能发动的不同能力个数,答案对
998244353
998244353
998244353 取模。你需要对于
1
≤
E
≤
V
∗
(
V
−
1
)
1\leq E \leq V*(V-1)
1≤E≤V∗(V−1)的所有
E
E
E 计算答案。
数据范围:
对于
10
10
10%的数据,
1
≤
V
≤
5
1 \leq V \leq 5
1≤V≤5
对于
30
30
30%的数据,
1
≤
V
≤
20
1 \leq V \leq 20
1≤V≤20
对于
60
60
60%的数据,
1
≤
V
≤
50
1 \leq V \leq 50
1≤V≤50
对于
100
100
100%的数据,
1
≤
V
≤
100
1 \leq V \leq 100
1≤V≤100
Analysis:
我们发现有用的仅仅是那个每一步的强连通分量序列。
一开始有
n
n
n个强连通分量,我们考虑用简单的情况构造。
从
1
1
1伸出去到
i
i
i一条链,然后剩余是单独的点也是强连通分量,在那条链上不断缩强连通分量,这样我们可以涵盖所有强连通分量变化的情况。
考虑每一步怎么变,要不是新加一个点到链上,或者往前连一条没用的边(保持连通分量不变),要不就是一个点往前连一条环边然后和前面的点缩到一起。注意连没用的边一定是在把所有的点加到链上之后,因为他们产生的效果相同,这里在转移的时候要特判。
那么我们考虑设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示当前用了
i
i
i条边,在链上前面的连通分量里的点是
1
1
1~
j
j
j,链上一共有
k
k
k个点。
那么转移就如上即可,注意判掉不合法情况,就是连没用边和环边时,边可能会超出能连数目。我们考虑链上有
n
n
n个点,有
i
i
i个点在环内。那么
n
n
n个点可以往后连,
i
i
i个点可以往前连,那么一共可以连
n
∗
(
n
−
1
)
+
i
∗
(
i
−
1
)
2
\frac{n*(n-1)+i*(i-1)}{2}
2n∗(n−1)+i∗(i−1)条边。
我们发现涉及到的状态一共也只有
V
4
V^4
V4个,因此暴力转移即可。
复杂度
O
(
V
4
)
O(V^4)
O(V4)
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 1e2 + 5;
const int mo = 998244353;
int f[N * (N - 1)][N][N];
int n;
inline int inc(int x,int y)
{ return x + y >= mo ? x + y - mo : x + y; }
inline int dec(int x,int y)
{ return x - y < 0 ? x - y + mo : x - y; }
int main()
{
freopen("counting.in","r",stdin);
freopen("counting.out","w",stdout);
scanf("%d",&n);
f[0][1][1] = 1;
for (int i = 0 ; i < n * (n - 1) ; ++i)
for (int j = 1 ; j <= min(n,i + 1) ; ++j)
for (int k = j ; k <= min(n,i + 1) ; ++k)
if (f[i][j][k])
{
if (i <= k + j - 2 && k < n) f[i + 1][j][k + 1] = inc(f[i + 1][j][k + 1],f[i][j][k]);
else if ((k * (k - 1) + j * (j - 1)) / 2 >= i + 1) f[i + 1][j][k] = inc(f[i + 1][j][k],f[i][j][k]);
for (int l = 1 ; l <= k - j ; ++l)
if ((k * (k - 1) + (j + l) * (j + l - 1)) / 2 >= i + 1) f[i + 1][j + l][k] = inc(f[i + 1][j + l][k],f[i][j][k]);
}
for (int i = 1 ; i <= n * (n - 1) ; ++i)
{
int ans = 0;
for (int j = 1 ; j <= min(n,i + 1) ; ++j)
for (int k = j ; k <= min(n,i + 1) ; ++k) ans = inc(ans,f[i][j][k]);
printf("%d ",ans);
}
return 0;
}