E. Johnny and Grandmaster(数学,思维)
程序员文章站
2023-12-27 13:57:39
...
题目传送门
题意: 给你n个数k[i],对于底数p,可以获得n个贡献pk[i] ,我们把这n个贡献分成两份,不要求均分,问你这两份的最小差值的绝对值是多少?
思路: 这里我们用到了幂的性质。首先因为大的数肯定要比小的数影响大,所以我们先安排大的数,假如我们把一个大数先放在了第一堆,那么接下来拿的数小于或等于这个数,并且有两种情况:1、剩下所有数拿完,还不能大于第一堆,那答案就是他们的差值。2、剩下所有数的和大于第一堆那个数,如果是这个情况那么我们必定能拿到一些数,使得他们的和要等于第一堆那个数,然后两者抵消,再判断剩下那些数就行。
上面讲了基本原理,下面看实现过程。我们假定第一堆是较大的那一堆数,用ans1表示目前的最小差值,如果差值为0,也就是两堆数的和一样大,那么下一个数就安排给第一堆,否则即差值不为0,就安排给第二堆。但是这题需要输出差值对1e9+7取模的结果,如果我们的差值为n*mod,他取模之后为0,就造成了误判,所以我们新增加一个模数,用ans2存在这个新模数下的差值,如果两者都为0,那么可以判断的确为0。(这样只是较大减少错误率,无法完全避免出错,类似双hash提高准确率)
#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define pii pair<int,int>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=2e6+5;
const int inf=0x3f3f3f3f;
const int mod1=1e9+7;
const int mod2=993471257;
const double eps=1e-20;
const double PI=acos(-1);
int k[N];
ll qpow(ll a,ll b,ll m)
{
ll res=1;
while(b)
{
if(b&1)
{
res=res*a%m;
}
a=a*a%m;
b>>=1;
}
return res;
}
signed main()
{
for(int t=read();t>0;t--)
{
int n=read(),p=read();
for(int i=1;i<=n;i++)
{
k[i]=read();
}
sort(k+1,k+n+1);
reverse(k+1,k+n+1);
ll ans1=0,ans2=0;
for(int i=1;i<=n;i++)
{
ll tmp1=qpow(p,k[i],mod1),tmp2=qpow(p,k[i],mod2);
if(!ans1&&!ans2)
{
ans1=tmp1;ans2=tmp2;
}
else
{
ans1=(ans1+mod1-tmp1)%mod1;
ans2=(ans2+mod2-tmp2)%mod2;
}
}
cout<<ans1<<endl;
}
}