Johnny and Grandmaster
或许更好的阅读体验
思路
这道题就是把一组数分成两个集合,使这两个集合的对p的次方的和的差的最小值,也就是求sum1−sum2sum1 - sum2sum1−sum2得最小值, 由于结果过大我们可能需要对结果取模。那么这题得关键在于我们应该如何分配这两个集合,也就是如何得到最优的sum1−sum2sum1 - sum2sum1−sum2的值。
我们先把给定的数组从大到小排序,我们一定可以得到pa[1]=pa[2]+pa[3]+pa[4]……+pa[x]p ^ {a[1]} = p ^ {a[2]} + p ^ {a[3]} + p ^ {a[4]} …… + p ^ {a[x]}pa[1]=pa[2]+pa[3]+pa[4]……+pa[x],这一点是显然成立的。
我们先分配最大的数到集合111中,接下来我们再分配其他的数到集合222,中,直到sum1==sum2sum1 == sum2sum1==sum2,我们再重新分配一个数到集合111中,重复如此操作我们就可以的到最小值。
这题的关键就在于我们如何判断这两个集合中的数和是相等的,容易想到sum1==sum2−>sum1−sum2==0sum1 == sum2 -> sum1 - sum2 == 0sum1==sum2−>sum1−sum2==0,于是我们好像可以利用这个点来完美的实现这个算法,但是很遗憾,wa在了test7,这里可能存在一个极大的误差,当我们的刚好是模数的时候,显然这里就错了,所以我们必须选定一个方法来避免这个错误,于是就有了,双模数判定差值是否为0。
代码
#include <bits/stdc++.h>using namespace std;typedef long long ll;inline ll read() {ll f = 1, x = 0;char c = getchar();while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}while(c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48);c = getchar();}return f * x;
}const int mod = 1e9 + 7, MOD = 1e9 + 3;//用两个模数来判断是否为零,
const int N = 1e6 + 10;ll a[N];ll qpow(ll a, ll n, ll mod) {ll ans = 1;while(n) {if(n & 1) ans = (ans * a) % mod;a = (a * a) % mod;n >>= 1;}return ans;
}int main() {// freopen("in.txt", "r", stdin);int t = read();while(t--) {int n = read(); ll p = read();for(int i = 1; i <= n; i++)a[i] = read();sort(a + 1, a + 1 + n, greater<ll> ());char *str = "okkkk";ll ans1 = 0, ans2 = 0;for(int i = 1; i <= n; i++) {if(!ans1 && !ans2) {//当这两个数同时为零的时候代表两个集合的差值为零。ans1 = (ans1 + qpow(p, a[i], mod)) % mod;ans2 = (ans2 + qpow(p, a[i], MOD)) % MOD;}else {ans1 = (ans1 + mod - qpow(p, a[i], mod)) % mod;ans2 = (ans2 + MOD - qpow(p, a[i], MOD)) % MOD;}}printf("%lld\n", ans1);}return 0;
}