题意
给定N个长度均为四的字符串,问他们恰好有K个字母不同的字符串有多少对(相同位置),其中字符集为0~9,a~z. n ≤ 50000
分析
十分显然一道题目,暴力
N2
嘛,优化起来很快。
我们考虑一种情况:有L个字符相同——注意并不是恰好——枚举其中不同的位置为
P1...PL
。这样的话求有多少对就很方便了,这是谁都能想到的方法,随便用点算法把原来所有字符串分成若干块(比如排序~~~),每块里的字符串都有指定位置上的字母相同,其他没有指定的位置我们不管。若有X个块,每个块有
Sizex
个字符串,那么这种情况的对数就是
∑sizex∗(sizex−1)/2
。
这样我们得到很多种情况的答案。很明显,要恰好,就是容斥一下的问题了,由于K最大只有4,所以明显可以过。
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int N=50005;
struct rec
{
int a[4],cmp;
}b[N];
int a[N][4],n,dd,i,j,c[4],dur,tt,kx,q1,l,rt;
long long ans,asw[20],as;
char s[4];
bool cmp(rec a,rec b){return a.cmp<b.cmp;}
int cmp1(rec a,rec b)
{
int i=0;
while (a.a[i]==b.a[i]&&i<3) i++;
return i;
}
int get(int x)
{
int i,j;
ans=0;
fo(i,1,n)
{
dur=0;
tt=-1;
fo(j,0,3)
if (c[j]!=-1)
{
dur=dur*36+a[i][j];
b[i].a[++tt]=a[i][j];
}
fo(j,0,3)
if (c[j]==-1)
{
dur=dur*36+a[i][j];
b[i].a[++tt]=a[i][j];
}
b[i].cmp=dur;
}
sort(b+1,b+1+n,cmp);
dur=1;
tt=0;
fo(i,2,n)
{
kx=cmp1(b[i],b[i-1]);
if (kx>=x)
dur++;
else
{
ans+=(long long)(dur)*(dur-1)/2;
dur=1;
}
}
ans+=(long long)(dur)*(dur-1)/2;
return ans;
}
int main()
{
scanf("%d%d\n",&n,&dd);
c[0]=c[1]=c[2]=c[3]=-1;
fo(i,1,n)
{
scanf("%s\n",s);
fo(j,0,3)
{
if (s[j]>='0'&&s[j]<='9')
a[i][j]=s[j]-'0';
else
a[i][j]=s[j]-'a'+10;
}
}
int d[17][5]={{0,0,0,0,0},{1,1,1,1,4},{1,1,1,-1,3},{1,1,-1,1,3},{1,-1,1,1,3},{-1,1,1,1,3},{1,1,-1,-1,2},{1,-1,1,-1,2},{1,-1,-1,1,2},{-1,1,1,-1,2},{-1,1,-1,1,2},{-1,-1,1,1,2},{1,-1,-1,-1,1},{-1,1,-1,-1,1},{-1,-1,1,-1,1},{-1,-1,-1,1,1},{-1,-1,-1,-1,0}};
//这是打情况表
fo(i,1,16)
{
fo(j,0,3) c[j]=d[i][j];
if (i==13)
{
i=i;
}
asw[i]=get(d[i][4]);
for(l=1;l<i;l++)
{
rt=0;
fo(j,0,3)
if (d[l][j]==d[i][j]&&d[l][j]==1)
rt++;
if (rt==d[i][j]&&d[l][j]>=d[i][j]+1)
asw[i]-=asw[l];
}
if (4-d[i][4]==dd)
as+=asw[i];
}
printf("%lld\n",as);
}
反思
这道题比赛的时候千辛万苦,改了5遍终于对了,最后对拍错了搞得我交题时还忧心忡忡。问题是很明显的。
1,审题不严谨,条件看着看着忘了。
2,构思的时候十分马虎,先想了算法,就是排序维护一种情况,然而把它打出来了以后我居然还不是很了解这个东西是干什么的,三番五次改程序各种地方,还怀疑是维护的姿势不对。
3,最后没时间了才紧张思考,按逻辑走,很快想通,思想也是十分简单,调试基本不用。
这道题浪费了大量时间做无用功,好的开端是成功的一半啊···