ISIJ 2018 超级跳棋(Training Round D6T5)
无忧公主 2018-07-10
题目名称:超级跳棋
文件名称:super.in / super.out
题目描述
小明是今年超级跳棋比赛的裁判,每轮有三名选手参加,结束时统计的分数一定是正整数,形如 a:b:c。小明的任务是在一块特殊的计分板上展示分数,他一共准备了 块写有正整数 的卡片,可供填写在 a、b、c 的位置上。此外,小明了解到超级跳棋的规则,他发现 a、b、c 之间最多相差 倍,例如 就是不合法的分数。为了检验他准备得是否充分,你需要计算小明可以在计分板上摆放出多少种不同的分数,即 (a,b,c) 这样的三元组有多少个。
限制
1s 256M
对于 20% 的数据,
对于另外 20% 的数据,
对于另外 30% 的数据,
对于另外 30% 的数据,
输入格式
第一行,两个整数 和
第二行, 个整数
输出格式
一个整数,表示 (a,b,c) 三元组的个数
输入样例
5 2
1 1 2 2 3
输出格式
9
样例解释
小明可以摆出的 a:b:c 有以下这些:1:1:2、1:2:1、2:1:1、1:2:2、2:1:2、2:2:1、2:2:3、2:3:2、3:2:2。由于 ,1 和 3 不能同时出现。
题解
首先将 排序,对于固定的最小值 ,a、b、c 三个数必然在 到 中选(即 ),当然对于 可以扫描线快速求得 。假设 必然取,然后可以根据 选 1 个还是 2 个还是 3 个,分类讨论并用乘法原理统计答案。需要同时维护当前 到 有几个是只出现了 1 次、有几个是出现了至少 2 次的(用 map 记录一个数出现了几次)。
#include <bits/stdc++.h>
using namespace std;
template <typename T> void read(T &t) {
char ch=getchar(); int f=1; t=0;
while ('0'>ch||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
do { (t*=10)+=ch-'0'; ch=getchar(); } while ('0'<=ch&&ch<='9'); t*=f;
}
typedef long long ll;
const int maxn=100010;
int n,k,x[maxn],a[maxn],sz;
map<int,int> m;
ll ans;
int main() {
read(n); read(k);
for (int i=1;i<=n;i++) {
read(x[i]); m[x[i]]++;
}
sort(x+1,x+(n+1));
for (int i=1;i<=n;i++)
if (x[i]!=x[i-1]) a[++sz]=x[i];
int pos=0,c1=0,c2=0;
for (int i=1;i<=sz;i++) {
while (pos<sz&&a[pos+1]<=(ll)a[i]*k) {
pos++;
if (m[a[pos]]>=2) c1++;
else c2++;
}
int v=0;
if (m[a[i]]>=2) v++;
int s=c1+c2-1;
ans+=3*(c1-v);
if (s>1) ans+=3LL*s*(s-1);
if (m[a[i]]>=2) ans+=3*(c1+c2-1);
if (m[a[i]]>=3) ans++;
if (m[a[i]]>=2) c1--; else c2--;
}
printf("%lld\n",ans);
return 0;
}