题目大意: 这个没图很难说啊qwq
题解
做法真是太秀了%%%
这题首先方向很重要,不能去想枚举某条边或某个面,因为边很多,太复杂,而面又难以合并,因为要考虑 个角。所以,这题要枚举角。
显然长度不同的词语是不能出现在同一个立方体里面的,那么就先按长度分类然后排序去重。
一个词语只有首尾的两个字母是有用的,不妨开一个数组记录一下, 表示以 作为首以 作为尾的单词数(注意这里的 是变量,下面同理)。然后大力枚举每个角的字母,然后利用 统计答案,就可以得到一个 的做法。
考虑优化这个枚举。
先上图(图丑莫喷,能看就好):
我们给每个角编个号,像上图那样。
假如从
号点出发,按广度优先搜索的顺序,我们可以将图分成四层:
第一层:
第二层:
第三层:
第四层:
我们惊奇地发现,第一层和第三层的四个点,他们之间是没有直接的边相连的!
那么又有这样一个性质:对于三个不相连的点,肯定存在一个点与他们三个都相连。
就像这样(其中
三个点之间没有连边):
我们不妨设现在有三个不相连的点 和一个连接他们三个的点 (参考上图理解),设 1表示 往点 上放字母 ,往点 上放字母 ,往点 上放字母 , 四点之间的三条边有多少种单词放法。
可能有点奇怪,往下看可能更好理解。
这样的话,我们大力枚举在 上放的字母,然后利用 数组就可以在 的时间内求出 数组。
再看回上面的问题,此时第一层和第三层一共有 个互不相连的点,我们不妨大力枚举这四个点上的字母,设他们的字母分别为 ,那么产生的贡献就是:
枚举这四个字母的时间复杂度是 的,那么总的时间复杂度就是 。
但是他还卡常。
因为我们的复杂度是满的,所以跑的相当的慢。
考虑到 其实都是一样的,那么不妨设 ,然后只求出 ,那么就可以节约 的时间。
既然 的参数都递增了,不妨求解的时候让枚举的 也递增,因为 这样的贡献与 的贡献是相同的。
但是要注意,当 时, 和 这两个是相同的方案,不能重复计算,所以对于一组递增的 ,他产生的贡献是 乘以 的不相同排列数(这个有点数论基础都知道怎么做吧)。
剩下不明白的结合代码理解吧:
#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
这三个字母 ↩︎