题意:
给一个序列,序列里的数都是 p k i p^{k_i} pki,把序列里的数划分为两个集合,使得差值最小。
题解:
首先对
k
i
k_i
ki排序,从大到小排列,对于
p
k
i
p^{k_i}
pki来说,如果
∑
j
=
i
+
1
n
p
k
j
>
=
p
k
i
\sum_{j=i+1}^{n}p^{k_j}>=p^{k_i}
∑j=i+1npkj>=pki,那么一定存在
l
l
l使得
∑
j
=
i
+
1
l
p
k
j
=
=
p
k
i
\sum_{j=i+1}^{l}p^{k_j}==p^{k_i}
∑j=i+1lpkj==pki。否则直接求差即可。
想象为
p
p
p进制,那么
p
k
i
p^{k_i}
pki是
(
1000
)
p
(1000)_p
(1000)p,其中第
k
i
k_i
ki位为
1
1
1,因为
k
j
(
j
>
i
&
&
k
j
<
k
i
)
k_j(j>i\&\&k_j<k_i)
kj(j>i&&kj<ki),使
l
l
l为最大的下标使得加上
p
k
l
p^{k_l}
pkl后的和大于等于
p
k
i
p^{k_i}
pki,那么此时加上
p
k
j
p^{k_j}
pkj的
p
p
p进制状态一定是——
(
00...
(
p
−
1
)
(
p
−
1
)
(
p
−
1
)
.
.
.
(
p
−
1
)
00..00
)
p
(00...(p-1)(p-1)(p-1)...(p-1)00..00)_p
(00...(p−1)(p−1)(p−1)...(p−1)00..00)p,在最低位的
(
p
−
1
)
(p-1)
(p−1)加上
1
1
1使得相等。
所以每次去寻找相等的左右部分即可,若找不到,则直接求差值即可。
因为如果模拟进位操作的话比较困难,而
p
k
i
=
p
∗
p
k
i
−
1
=
p
2
∗
p
k
i
−
2
p^{k_i}=p*p^{k_i-1}=p^2*p^{k_i-2}
pki=p∗pki−1=p2∗pki−2,所以将
p
k
i
p^{k_i}
pki降次,从而求是否有
p
x
p^x
px个
p
k
i
−
x
p^{k_i-x}
pki−x。
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
ll qpow(ll x, ll n) {
ll res = 1;
while (n) {
if (n&1) {
res = (res*x) % mod;
}
x = (x*x) % mod;
n /= 2;
}
return res;
}
void solve() {
int n;
ll p;
cin >> n >> p;
vector<ll> k(n);
for (int i = 0; i < n; i++) {
cin >> k[i];
}
if (p == 1) {
cout << n%2 << endl;
return ;
}
sort(k.begin(), k.end());
vector<int> l, r;
int idx = n-1;
while (idx >= 0) {
r.push_back(k[idx]);
ll A = 1;
int x = k[idx];
int i;
for (i = idx-1; i >= 0; i--) {
l.push_back(k[i]);
// 当前左边部分值为A*p^x,右边部分值为p^k[i]
// A*p^x = A*(p^diff) * p^k[i]
int diff = x - k[i];
x = k[i];
// 当前有A个p^x,转换为(A*p^diff个p^k[i])
// 若A>=n,则剩下所有的数的和都小于当前左边部分值
// log(n)<20,循环最多不超过20次
while (diff && A < n) {
A *= p;
--diff;
}
--A; //消耗当前的一个p^k[i]
if (diff == 0 && A == 0) {
break;
}
}
idx = i-1;
}
ll ans = 0;
for (auto x : r) {
ans = (ans + qpow(p, x)) % mod;
}
for (auto x : l) {
ans = (ans - qpow(p, x) + mod) % mod;
}
cout << ans << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}