AC1230 k倍區間(藍橋杯)
技術標籤:題目筆記
題意:問有多少段的和能被k整除
這個題跟我入隊考試的題非常的像。
首先他問我們某一段的和是不是一個k的倍數,那麼一段和我們很自然的就聯想到字首和了。而且我們知道,如果對每一個字首和都進行%k處理,那麼就一定會使得某些數是0(假設這個數的位置是i),那麼就說明從1到i的所有的數的和都是k的倍數。那麼我們也會得到有的和並不是0,而是1~(k - 1)中的數。但是不妨礙裡面有相同的數。
假設 s[i] % k = x,那麼我們有取餘的定義可知:
s
[
i
]
/
k
=
n
⋯
x
k
∗
n
+
x
=
s
[
i
]
s[i] / k = n \cdots x \\ k * n + x = s[i]
那麼假設還有一個數s[j]的取餘結果也是x,同理
k
∗
m
+
x
=
s
[
j
]
k * m + x = s[j]
k∗m+x=s[j]
兩式相減得:
k
∗
n
−
k
∗
m
=
s
[
i
]
−
s
[
j
]
k
∗
(
n
−
m
)
=
s
[
i
]
−
s
[
j
]
k*n - k*m=s[i]-s[j]\\ k*(n-m) = s[i]-s[j]
k∗n−k∗m=s[i]−s[j]k∗(n−m)=s[i]−s[j]
那麼就可以得到:
(
s
[
i
]
−
s
[
j
]
)
/
k
=
(
n
−
m
)
⋯
0
(s[i]-s[j]) / k = (n - m) \cdots 0
即:
(
s
[
i
]
−
s
[
j
]
)
%
k
=
0
(s[i]-s[j]) \% k = 0
(s[i]−s[j])%k=0
那麼我們就能得到一下結論:
如果某個餘數在出現過之後在它後面再次出現過,那麼 [ i , j ]這個區間就可以是個k倍區間。
所以說,假設一個餘數出現過 m 次,那麼同時包含這個餘數的k倍區間的個數就是 (m - 1) * m / 2次(因為出現的第一次無法與前面組成k倍區間)
假設出現了5次
1 2 3 4 5
1 2
1 3
2 3
1 4
2 4
3 4
1 5
2 5
3 5
4 5
每個位置與前面的組成答案,就是 1 + 2 + 3 + 4 + …(n - 2) * (n -1) = (n - 1) * n / 2
當0為餘數的時候,就是它出現的次數就是(m + 1) * m / 2(餘數是0 的時候,第一次出現的那個位置也可以組成一個k倍區間)
#include <iostream>
#include <vector>
#define ll long long
using namespace std;
int n;
ll a[100000 + 10];
int k;
ll s[100000 + 10]; //看資料範圍我們知道,a[i]最大100000,你們到最後s[i]最大到100000 * 100000 = 10^10超出int
vector<ll> wei[100000 + 10]; //存第幾位出現的,其實這裡只存出現的個數就行;讀者可以自行更改一下
int main(){
cin >> n >> k;
for (int i = 1; i <= n; i++){
cin >> a[i];
s[i] = (s[i - 1] % k + a[i] % k) % k;
wei[s[i]].push_back(i);
}
ll x = wei[0].size();
ll cnt = x * (x + 1) / 2;
for (int i = 1; i <= k; i++){
x = wei[i].size();
cnt += ((x - 1) * x / 2);
}
cout << cnt;
return 0;
}
其實這種做法跟y總的思路差不多,