E. Johnny and Grandmaster 数论+思维

这道题的思路来源于一个叫zbr的大佬 真的挺秒的  仔细想想也不是很难 但是没有想到如此简便的做法

首先我们要从大到小排序 这个应该没问题 因为大数的权重远远比小的大  所以先分配大的在分配小的

其次 我们要清楚 对于一个大数  如果我们把他分配给一个集合  另外一个集合就要想办法凑出这个数 根据p^k这个特殊性 假设我们这个一个大数是 p^k 那么比它小的数 要么刚好凑成它 要么不够凑  这是显然易见的 如果不够凑 答案就是 p^k减去所有比它小的数 否则 我们把p^k 凑出来 在重置差值为0  再继续这个过程 具体可以代码  注意差值为0的时候 不能光用当前值来判断 得再搞一个模数  这样可以保证 差值为0 (否则你的差值为mod的倍数 求出来也是0) 类似双hash

具体看代码 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//两个模数
const ll mod1=1000000007;
const ll mod2=983231753;
ll qpow(ll a,ll b,ll mod){
	ll ret = 1;
	while(b){
		if(b&1) ret=(ret%mod*a%mod)%mod;
		a=(a%mod*a%mod)%mod;
		b>>=1; 
	}
	return ret;
}
const int N = 1e6+10;
ll a[N];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n,p;
		scanf("%d%d",&n,&p);
		ll ans=0,ans1=0;
		for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
        //降序排列
		sort(a+1,a+1+n);
		reverse(a+1,a+1+n);
		for(int i = 1; i <= n; i++){
			ll temp=qpow(p,a[i],mod1),temp1=qpow(p,a[i],mod2);
            //当差值为0的时候 我们把大数分配给一个集合
			if(ans==0&&ans1==0) ans=temp,ans1=temp1;	
            //当差值不为0,我们去凑那个大数(只有刚好凑出和不够两种情况) 凑一个减一个	
            else ans=(ans-temp+mod1)%mod1,ans1=(ans1-temp1+mod2)%mod2;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/106567230