E. Johnny and Grandmaster(数学,思维)

题目传送门

题意: 给你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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值