正题
考场上我只会枚举上下两层的正方形,然后暴力合并。
这题的题解思考方式很强:
首先正着反着插进去拍一遍序,去一下重。
考虑黑白染色,分成两组互不相关的点。
然后对于一个点,枚举附近三个点的方案数:表示有分别一条边连到a,b,c的可选点方案数,我们并不需要在乎当前点是什么。
上面这个东西可以用预处理出来。
枚举一组点,贡献的答案显然就是:
接着发现你被卡常了!
实际可以很容易的减少常数:
六组其实是相等的,所以我们可以保证
来减少大约
的预处理常数。
而枚举也是这样子的。保证
,就会减少大约
的求解常数,但是在求解过程中,
实际上是可以调换的,而且可能存在重复,乘上一个系数就可以了。
#include<bits/stdc++.h>
using namespace std;
vector<string> V[11];
long long E[62][62],D[62][62][62],t[62];
string s;
int n;
const long long mod=998244353;
int chk(char x){
if(x>='a' && x<='z') return x-'a';
if(x>='A' && x<='Z') return x-'A'+26;
if(x>='0' && x<='9') return x-'0'+52;
}
int main(){
scanf("%d",&n);
int mmax=0;
for(int i=1;i<=n;i++){
cin>>s;
int len=s.length();
V[len].push_back(s);
for(int j=0;j<len/2;j++) swap(s[j],s[len-1-j]);
mmax=max(mmax,len);
V[len].push_back(s);
}
long long tot=0;
for(int i=3;i<=10;i++){
if(V[i].size()==0) continue;
sort(V[i].begin(),V[i].end());
memset(E,0,sizeof(E));
E[chk(V[i][0][0])][chk(V[i][0][i-1])]++;
for(int j=1;j<V[i].size();j++)
if(V[i][j]!=V[i][j-1]) E[chk(V[i][j][0])][chk(V[i][j][i-1])]++;
memset(D,0,sizeof(D));
for(int a=0;a<62;a++)
for(int b=0;b<62;b++) if(E[a][b])
for(int c=b;c<62;c++) if(E[a][c])
for(int d=c;d<62;d++) if(E[a][d])
D[b][c][d]+=1ll*E[a][b]*E[a][c]%mod*E[a][d]%mod,D[b][c][d]>=mod?D[b][c][d]-=mod:0;
int tmp=24;
long long ans=0;
for(int a=0;a<62;a++){
t[a]++;tmp/=t[a];
for(int b=a;b<62;b++){
t[b]++;tmp/=t[b];
for(int c=b;c<62;c++){
t[c]++;tmp/=t[c];
for(int d=c;d<62;d++){
t[d]++;tmp/=t[d];
ans+=1ll*tmp*D[a][b][c]%mod*D[a][b][d]%mod*D[a][c][d]%mod*D[b][c][d]%mod;
tmp*=t[d];t[d]--;
}
tmp*=t[c];t[c]--;
}
tmp*=t[b];t[b]--;
}
tmp*=t[a];t[a]--;
}
tot+=ans;
}
printf("%lld\n",tot%mod);
}
本文探讨了一种在考场场景下,通过枚举上下两层正方形并暴力合并的初始策略,进而深入分析了如何通过黑白染色、预处理方案数及常数优化等技巧,将复杂度有效降低的方法。文章详细介绍了使用C++实现的算法细节,包括输入处理、排序、去重、方案数计算及最终答案的求解过程。
296

被折叠的 条评论
为什么被折叠?



