题目大意: 这个没图很难说啊qwq
题解
做法真是太秀了%%%
这题首先方向很重要,不能去想枚举某条边或某个面,因为边很多,太复杂,而面又难以合并,因为要考虑 4 4 4 个角。所以,这题要枚举角。
显然长度不同的词语是不能出现在同一个立方体里面的,那么就先按长度分类然后排序去重。
一个词语只有首尾的两个字母是有用的,不妨开一个数组记录一下, e [ a ] [ b ] e[a][b] e[a][b] 表示以 a a a 作为首以 b b b 作为尾的单词数(注意这里的 a , b a,b a,b 是变量,下面同理)。然后大力枚举每个角的字母,然后利用 e e e 统计答案,就可以得到一个 O ( 6 1 8 ) O(61^8) O(618) 的做法。
考虑优化这个枚举。
先上图(图丑莫喷,能看就好):
我们给每个角编个号,像上图那样。
假如从
1
1
1 号点出发,按广度优先搜索的顺序,我们可以将图分成四层:
第一层:
1
~~~1
1
第二层:
2
3
4
2~3~4
2 3 4
第三层:
5
6
7
5~6~7
5 6 7
第四层:
8
~~~8
8
我们惊奇地发现,第一层和第三层的四个点,他们之间是没有直接的边相连的!
那么又有这样一个性质:对于三个不相连的点,肯定存在一个点与他们三个都相连。
就像这样(其中
A
,
B
,
C
A,B,C
A,B,C 三个点之间没有连边):
我们不妨设现在有三个不相连的点 A , B , C A,B,C A,B,C 和一个连接他们三个的点 D D D(参考上图理解),设 f [ a ] [ b ] [ c ] f[a][b][c] f[a][b][c] 1表示 往点 A A A 上放字母 a a a,往点 B B B 上放字母 b b b,往点 C C C 上放字母 c c c, A , B , C , D A,B,C,D A,B,C,D 四点之间的三条边有多少种单词放法。
可能有点奇怪,往下看可能更好理解。
这样的话,我们大力枚举在 A , B , C , D A,B,C,D A,B,C,D 上放的字母,然后利用 e e e 数组就可以在 O ( 6 1 4 ) O(61^4) O(614) 的时间内求出 f f f 数组。
再看回上面的问题,此时第一层和第三层一共有 4 4 4 个互不相连的点,我们不妨大力枚举这四个点上的字母,设他们的字母分别为 a , b , c , d a,b,c,d a,b,c,d,那么产生的贡献就是: p = f [ a ] [ b ] [ c ] × f [ a ] [ b ] [ d ] × f [ a ] [ c ] [ d ] × f [ b ] [ c ] [ d ] p=f[a][b][c] \times f[a][b][d] \times f[a][c][d] \times f[b][c][d] p=f[a][b][c]×f[a][b][d]×f[a][c][d]×f[b][c][d]
枚举这四个字母的时间复杂度是 O ( 6 1 4 ) O(61^4) O(614) 的,那么总的时间复杂度就是 O ( 6 1 4 ) O(61^4) O(614)。
但是他还卡常。
因为我们的复杂度是满的,所以跑的相当的慢。
考虑到 f [ a ] [ b ] [ c ] , f [ a ] [ c ] [ b ] , . . . , f [ c ] [ b ] [ a ] f[a][b][c],f[a][c][b],...,f[c][b][a] f[a][b][c],f[a][c][b],...,f[c][b][a] 其实都是一样的,那么不妨设 a ≤ b ≤ c a\leq b \leq c a≤b≤c,然后只求出 f [ a ] [ b ] [ c ] f[a][b][c] f[a][b][c],那么就可以节约 5 6 × 6 1 4 \frac 5 6 \times 61^4 65×614 的时间。
既然 f f f 的参数都递增了,不妨求解的时候让枚举的 a , b , c , d a,b,c,d a,b,c,d 也递增,因为 ( b , a , c , d ) , ( b , c , d , a ) (b,a,c,d),(b,c,d,a) (b,a,c,d),(b,c,d,a) 这样的贡献与 ( a , b , c , d ) (a,b,c,d) (a,b,c,d) 的贡献是相同的。
但是要注意,当 a = b a=b a=b 时, ( a , b , c , d ) (a,b,c,d) (a,b,c,d) 和 ( b , a , c , d ) (b,a,c,d) (b,a,c,d) 这两个是相同的方案,不能重复计算,所以对于一组递增的 ( a , b , c , d ) (a,b,c,d) (a,b,c,d),他产生的贡献是 p p p 乘以 a , b , c , d a,b,c,d a,b,c,d 的不相同排列数(这个有点数论基础都知道怎么做吧)。
剩下不明白的结合代码理解吧:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 62
#define ll long long
#define mod 998244353
int n;
vector<string>s[11];
string ss;
int ch(char x)//将字母转化为数字来存
{
if(x>='0'&&x<='9')return x-'0';//0~9 : 0~9
if(x>='a'&&x<='z')return x-'a'+10;//a~z : 10~35
if(x>='A'&&x<='Z')return x-'A'+36;//A~Z : 36~61
}
ll e[maxn][maxn],f[maxn][maxn][maxn];//数组意义如上所述
ll times[maxn],ans=0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>ss;
s[ss.length()].push_back(ss);
//因为不在意阅读顺序,所以这个串反过来也能用,也要记录下来
for(int i=0;i<ss.length()/2;i++)
swap(ss[i],ss[ss.length()-i-1]);
s[ss.length()].push_back(ss);
}
for(int i=3;i<=10;i++)
if(s[i].size()>0)
{
memset(e,0,sizeof(e));//记得每次要清空
memset(f,0,sizeof(f));
sort(s[i].begin(),s[i].end());//排序
for(int j=0;j<s[i].size();j++)//去重+处理e数组
if(j==0||s[i][j]!=s[i][j-1])e[ch(s[i][j][0])][ch(s[i][j][i-1])]++;
for(int a=0;a<maxn;a++)//处理f数组
for(int b=0;b<maxn;b++)if(e[a][b]>0)
for(int c=b;c<maxn;c++)if(e[a][c]>0)
for(int d=c;d<maxn;d++)if(e[a][d]>0)
f[b][c][d]=(f[b][c][d]+e[a][b]*e[a][c]%mod*e[a][d]%mod)%mod;
ll p=24;
for(int a=0;a<maxn;a++)
{
times[a]++;p/=times[a];//用来解决不相同排列数
for(int b=a;b<maxn;b++)
{
times[b]++;p/=times[b];
for(int c=b;c<maxn;c++)
{
times[c]++;p/=times[c];
for(int d=c;d<maxn;d++)
{
times[d]++;p/=times[d];
ans=(ans+p*f[a][b][c]%mod*f[a][b][d]%mod*f[a][c][d]%mod*f[b][c][d]%mod)%mod;
p*=times[d];times[d]--;
}
p*=times[c];times[c]--;
}
p*=times[b];times[b]--;
}
p*=times[a];times[a]--;
}
}
printf("%lld",ans);
}
还是要提醒,这里的 a , b , c a,b,c a,b,c 是变量而不是
a,b,c
这三个字母 ↩︎