P7617 [COCI2011-2012#2] KOMPIĆI 题解
题目
链接
https://www.luogu.com.cn/problem/P7617
字面描述
题目描述
给定 N N N 个正整数 A 1 , A 2 , . . . , A N A_1,A_2,...,A_N A1,A2,...,AN,求有多少整数对 ( i , j ) (i,j) (i,j),满足以下条件:
- 1 ≤ i < j ≤ N 1 \le i < j \le N 1≤i<j≤N
- A i A_i Ai 和 A j A_j Aj 至少有一位数字是相同的(不一定要在相同的数位)。
输入格式
输入的第一行包含一个正整数 N N N。
接下来 N N N 行,每行包含一个正整数 A i A_i Ai。
输出格式
输出一行一个整数,表示满足条件的整数对。
样例 #1
样例输入 #1
3
4
20
44
样例输出 #1
1
样例 #2
样例输入 #2
4
32
51
123
282
样例输出 #2
4
提示
【样例解释】
样例 1 中,满足要求的整数对为 ( 1 , 3 ) (1,3) (1,3)。
样例 2 中,满足要求的整数对为 ( 1 , 3 ) (1,3) (1,3), ( 1 , 4 ) (1,4) (1,4), ( 2 , 3 ) (2,3) (2,3), ( 3 , 4 ) (3,4) (3,4)。
【数据范围】
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 6 1 \le N \le 10^6 1≤N≤106, 1 ≤ A i ≤ 1 0 18 1 \le A_i \le 10^{18} 1≤Ai≤1018。
【说明】
本题分值按 COCI 原题设置,满分 120 120 120。
题目译自 COCI2011-2012 CONTEST #2 T4 KOMPIĆI。
思路
部分分
创建一个cnt数组,针对每一个数分解掉他是否含0~9
在按照题目需求两数对拍这9个数,统计答案
所有分
上面的思路是好但是最高时间复杂度为O(10^15),爆超
我们在往深一层次想一想,每个数只需要维护10个状态0~9;
完全可以状态压缩;
将他压缩成二进制数后,存入一个大小为1024(2^10)的桶里;
再将0 ~ 1023和0 ~1023分别对拍一下,如果含有同一个数字,最终答案+=
c
n
t
i
∗
c
n
t
j
cnt_i*cnt_j
cnti∗cntj
最后对于每个i(0<=i<1023) 最终答案+=
c
n
t
i
∗
(
c
n
t
i
−
1
)
/
2
cnt_i*(cnt_i-1)/2
cnti∗(cnti−1)/2 (自己的元素)
最终答案存的就是结果 最终答案存的就是结果 最终答案存的就是结果
代码实现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1024+10;
const int M=1024;
int n,op;
ll x,ans;
int k[maxn];//桶
int main(){
scanf("%d",&n);
while(n--){
scanf("%lld",&x);
op=0;
//状态压缩
while(x){
op|=1<<(x%10);
x/=10;
}
//装进桶里
++k[op];
}
//所有数对拍
for(int i=0;i<M;i++){
for(int j=i+1;j<M;j++){
//如果有相同数字,更新答案
if(i&j)ans+=(ll)k[i]*k[j];
}
}
// 与自己在对拍,排列组合算答案
for(int i=0;i<M;i++) ans+=(ll)k[i]*(k[i]-1)>>1;
//输出答案
printf("%lld\n",ans);
return 0;
}