【CEOI2015】pin
Cry_For_theMoon · · 题解
传送门
今天机房T3出了这道题,纪念第一次机房模拟赛AK
这道题一下想到容斥的都是数学神仙吧。
一步步分析。当 15% 的暴力打完以后,我们发现其实
"有一个不同"的关系和"有三个相同"是一一对应的。考虑计算三个字符相同的密码的数,枚举这三个字符的位置,只有
inline int hash3(char a,char b,char c){
return hash1(a)*36*36+hash1(b)*36+hash1(c);
}
相应的我们写出了这样的代码:
//3位相同
for(int i=1;i<=4;i++){
for(int j=i+1;j<=4;j++){
for(int k=j+1;k<=4;k++){
clearmemory();
for(int l=1;l<=n;l++){
size[hash3(pins[l][i],pins[l][j],pins[l][k])]++;
}
for(int l=0;l<=LIM;l++){
cnt[3] = cnt[3] + size[l] * (size[l]-1)/2;
}
}
}
}
这个 clearmemory() 是memset。
两位和一位的哈希函数同理,完整代码会在文末给出。
这个时候不是输出
题目中的一句话点醒了我:“任意两个字符串都有多于一个位置不相同”
,再想一下第一个是
顺着下去推
接下来是
最后
最后输出
//CEOI,2010
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=6e4+10,LIM=5e4;
int n,d;
char pins[MAXN][10];
long long size[MAXN],cnt[10],ans[10];
inline void clearmemory(){
memset(size,0,sizeof size);
}
inline int hash1(char a){
if(a>='0'&&a<='9'){
return a-'0';
}else{
return a-'a'+10;
}
}
inline int hash2(char a,char b){
return hash1(a)*36+hash1(b);
}
inline int hash3(char a,char b,char c){
return hash1(a)*36*36+hash1(b)*36+hash1(c);
}
int main(){
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++){
scanf("%s",pins[i]+1);
}
//1位相同
for(int i=1;i<=4;i++){
clearmemory();
for(int j=1;j<=n;j++){
size[hash1(pins[j][i])]++;
}
for(int j=0;j<=LIM;j++){
cnt[1] = cnt[1] + size[j] * (size[j]-1)/2;
}
}
//2位相同
for(int i=1;i<=4;i++){
for(int j=i+1;j<=4;j++){
clearmemory();
for(int k=1;k<=n;k++){
size[hash2(pins[k][i],pins[k][j])]++;
}
for(int k=0;k<=LIM;k++){
cnt[2] = cnt[2] + size[k] * (size[k]-1)/2;
}
}
}
//3位相同
for(int i=1;i<=4;i++){
for(int j=i+1;j<=4;j++){
for(int k=j+1;k<=4;k++){
clearmemory();
for(int l=1;l<=n;l++){
size[hash3(pins[l][i],pins[l][j],pins[l][k])]++;
}
for(int l=0;l<=LIM;l++){
cnt[3] = cnt[3] + size[l] * (size[l]-1)/2;
}
}
}
}
ans[3] = cnt[3];
ans[2] = cnt[2]-ans[3]*3;
ans[1] = cnt[1]-ans[2]*2-ans[3]*3;
ans[0] = (long long)n*(n-1)/2 - ans[1]-ans[2]-ans[3];
printf("%lld",ans[4-d]);
return 0;
}