超級跳棋

ISIJ 2018 超級跳棋(Training Round D6T5)

題目描述

小明是今年超級跳棋比賽的裁判,每輪有三名選手參加,結束時統計的分數一定是正整數,形如 a:b:c。小明的任務是在一塊特殊的計分板上展示分數,他一共準備了 n 塊寫有正整數 x1x2xn 的卡片,可供填寫在 a、b、c 的位置上。此外,小明瞭解到超級跳棋的規則,他發現 a、b、c 之間最多相差 k 倍,例如 ca>k 就是不合法的分數。爲了檢驗他準備得是否充分,你需要計算小明可以在計分板上擺放出多少種不同的分數,即 (a,b,c) 這樣的三元組有多少個。

限制

1s 256M

對於 20% 的數據,3n100,000k=11xi100,000

對於另外 20% 的數據,3n1001k1001xi100

對於另外 30% 的數據,3n100,0001kxi109xi

對於另外 30% 的數據,3n100,0001kxi109

輸入格式

第一行,兩個整數 nk

第二行,n 個整數 x1x2xn

輸出格式

一個整數,表示 (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。由於 k=2 ,1 和 3 不能同時出現。

題解

首先將 xi 排序,對於固定的最小值 xi ,a、b、c 三個數必然在 xixj 中選(即 xj+1xi>k ),當然對於 i 可以掃描線快速求得 j 。假設 xi 必然取,然後可以根據 xi 選 1 個還是 2 個還是 3 個,分類討論並用乘法原理統計答案。需要同時維護當前 xixj 有幾個是隻出現了 1 次、有幾個是出現了至少 2 次的(用 map 記錄一個數出現了幾次)。

這道題很睿智,不知道爲什麼放在第三題

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read(){
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int a[100005],b[100005],d[100005],p,k,n;
int s[100005];
ll ans=0;
int find(int x){
    int l=1,r=p,ans=-1;
    ll q=(ll)x*k;
    while (l<=r){
        int mid=(l+r)>>1;
        if (d[mid]<=q){ l=mid+1; ans=mid;} else r=mid-1;
    }
    return ans;
}
int main(){
    n=read(),k=read();
    for (int i=1;i<=n;i++)
    a[i]=read();
    sort(a+1,a+1+n);
    for (int i=1;i<=n;i++){
        if (a[i]!=a[i-1]) p++,d[p]=a[i];
        b[p]++;
    }
    for (int i=1;i<=p;i++){
        if (b[i]>=2) s[i]=s[i-1]+1;
        else s[i]=s[i-1]; 
    }
    for (int i=1;i<=p;i++){
        int q1=find(d[i]);
        if (q1==-1||q1<i) continue;
        int kd=q1-i;
        ans+=1ll*kd*(kd-1)*3;
        ans+=1ll*(s[q1]-s[i])*3;
        if (b[i]>=2) ans+=1ll*kd*3;
        if (b[i]>=3) ans++;
    }
    cout<<ans<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章