赛后看感觉是道水题,然而为撒考场上我们并没有想到枚举一个矩形的左上角或者右下角,再预处理出nxt[i][j],down[i][j]表示i,j点行和列上下一个会出现重复的位置,然后n*m*52去扫一遍,每个左上角顶点所能构成的答案矩形.
#include<cstdio>
#include<cstring>
#define maxl 1010
int n,m;
int last[maxl],lmt[maxl];
int a[maxl][maxl];
int nxt[maxl][maxl],down[maxl][maxl];
char s[maxl];
long long ans;
inline int gank(char ch)
{
if(ch>='A' && ch<='Z') return ch-'A'+1;
else return ch-'a'+1+26;
}
inline int min(int a,int b)
{
if(a<b)
return a;
else
return b;
}
inline void prework()
{
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
a[i][j]=gank(s[j]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=52;j++)
last[j]=m+1;
nxt[i][m+1]=m+1;
for(int j=m;j>=1;j--)
{
nxt[i][j]=min(nxt[i][j+1],last[a[i][j]]);
last[a[i][j]]=j;
}
}
for(int j=1;j<=m;j++)
{
for(int i=1;i<=52;i++)
last[i]=n+1;
down[n+1][j]=n+1;
for(int i=n;i>=1;i--)
{
down[i][j]=min(down[i+1][j],last[a[i][j]]);
last[a[i][j]]=i;
}
}
}
inline void mainwork()
{
ans=0;int sz;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
lmt[j]=down[i][j];
for(int k=j+1;k<nxt[i][j];k++)
lmt[k]=min(lmt[k-1],down[i][k]);
sz=nxt[i][j]-j;
for(int k=i;k<down[i][j];k++)
{
sz=min(nxt[k][j]-j,sz);
while(lmt[j+sz-1]<=k && sz>0)
sz--;
if(sz==0)
break;
ans+=sz;
}
}
}
inline void print()
{
printf("%lld\n",ans);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
prework();
mainwork();
print();
}
return 0;
}