题意:
给一个序列,序列里的数都是pkip^{k_i}pki,把序列里的数划分为两个集合,使得差值最小。
题解:
首先对kik_iki排序,从大到小排列,对于pkip^{k_i}pki来说,如果
∑j=i+1npkj>=pki\sum_{j=i+1}^{n}p^{k_j}>=p^{k_i}∑j=i+1npkj>=pki,那么一定存在lll使得∑j=i+1lpkj==pki\sum_{j=i+1}^{l}p^{k_j}==p^{k_i}∑j=i+1lpkj==pki。否则直接求差即可。
想象为ppp进制,那么pkip^{k_i}pki是(1000)p(1000)_p(1000)p,其中第kik_iki位为111,因为kj(j>i&&kj<ki)k_j(j>i\&\&k_j<k_i)kj(j>i&&kj<ki),使lll为最大的下标使得加上pklp^{k_l}pkl后的和大于等于pkip^{k_i}pki,那么此时加上pkjp^{k_j}pkj的ppp进制状态一定是——(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)加上111使得相等。
所以每次去寻找相等的左右部分即可,若找不到,则直接求差值即可。
因为如果模拟进位操作的话比较困难,而pki=p∗pki−1=p2∗pki−2p^{k_i}=p*p^{k_i-1}=p^2*p^{k_i-2}pki=p∗pki−1=p2∗pki−2,所以将pkip^{k_i}pki降次,从而求是否有pxp^xpx个pki−xp^{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();
}
}
探讨了如何将一个由特定形式元素组成的序列划分为两部分,目标是最小化这两部分之间的差值。通过排序和进位模拟,算法有效地找到最佳划分点,适用于竞赛编程中的优化问题。
515

被折叠的 条评论
为什么被折叠?



