タイトル説明
問題の解決策
最初の分類の長さに応じて、我々は唯一の文字がエンドツーエンドで何を気にするので、我々は記録することができますので、その各文字列の長所と短所の長さのために、我々は、すべてのダウンとデエンファシスを置くことができ、正と負として読み取ることができます の開始を示します 、エンディング 列どのように多くの。
そして、これは立方体であり、我々はすべての角度、そして統計プログラムを検討することができますが、実際には、我々は最初に、以下のプログラム状図の数を数えることができるもので最も暴力的な列挙交差点プットの関係なく、どのようなエッジ、もしその残りの頂点 、その後、私たちはこの番号を入れてどのように多くの種類のプログラムの計画します 。
その後、我々は以下の形の、我々はにキューブことができます見つけることができます 番目の図形パターンを、頂点 、頂点の塗りの残りの部分であれば 、その後、プログラムの番号です 。
そして、私たちはしばしば、このようなシークなど、カードが必要 のための時間 即可,求方案数的时候让 即可,注意求方案数要乘上排列数。
代码
#include <bits/stdc++.h>
#define U unsigned long long
using namespace std;
const U B=793999;
const int P=998244353,N=62;
char ch[15];map<U,int>mp;
int n,m,g[15][N][N],f[N][N][N],t[N],s;
int X(int x){return x>=P?x-P:x;}
int G(char x){
if (x>='a' && x<='z') return x-'a';
if (x>='A' && x<='Z') return x-'A'+26;
return (x^48)+52;
}
int main(){
cin>>n;
for (int i=1;i<=n;i++){
scanf("%s",ch+1);
m=strlen(ch+1);U v=0;
for (int j=1;j<=m;j++) v=v*B+ch[j];
if (!mp.count(v))
mp[v]=1,g[m][G(ch[1])][G(ch[m])]++;
v=0;
for (int j=m;j;j--) v=v*B+ch[j];
if (!mp.count(v))
mp[v]=1,g[m][G(ch[m])][G(ch[1])]++;
}
for (int x=3;x<=10;x++){
memset(f,0,sizeof f);
for (int a=0;a<N;a++)
for (int b=0;b<N;b++) if (g[x][a][b])
for (int c=b;c<N;c++) if (g[x][a][c])
for (int d=c;d<N;d++) if (g[x][a][d])
f[b][c][d]=X(f[b][c][d]+1ll*g[x][a][b]*g[x][a][c]%P*g[x][a][d]%P);
int p=24;
for (int a=0;a<N;a++){
t[a]++;p/=t[a];
for (int b=a;b<N;b++){
t[b]++;p/=t[b];
for (int c=b;c<N;c++) if (f[a][b][c]){
t[c]++;p/=t[c];
for (int d=c;d<N;d++){
t[d]++;p/=t[d];
s=X(s+1ll*p*f[a][b][c]%P*f[a][b][d]%P*f[a][c][d]%P*f[b][c][d]%P);
p*=t[d];t[d]--;
}
p*=t[c];t[c]--;
}
p*=t[b];t[b]--;
}
p*=t[a];t[a]--;
}
}
cout<<s<<endl;return 0;
}