E. Johnny and Grandmaster

https://codeforces.com/contest/1362/problem/E

题目意思就是给一个长度为n的序列k , 然后呢要求将这些数分为两个集合A、B,使得两个集合差值的绝对值最小,也就是 min ⁡ ∣ ∑ i ∈ A p k [ i ] − ∑ j ∈ B p k [ j ] ∣ \min|\sum_{i\in A}p^{k[i]} - \sum_{j\in B} p^{k[j]}| miniApk[i]jBpk[j]

做法就是将这个题目呢看成P进制表示, p k [ i ] p^{k[i]} pk[i]也就是第k[i]为上面是1,
在进制表示里面,我们从左到右下标增加1 . 2 . 3 . 4 . 5… n
策略 :

  1. 按照k值降序排列
  2. p k [ i ] > = p k [ i + 1 ] + p k [ i + 2 ] + . . . + p k [ j ] p^{k[i]} >= p^{k[i + 1]} + p^{k[i + 2]} + ... + p ^ {k[j]} pk[i]>=pk[i+1]+pk[i+2]+...+pk[j] , 在进制里面我们一定可以得到 ∑ j < i p j = p i \sum_{j < i}p^{j} = p^{i} j<ipj=pi , 比如 2 3 = 2 2 + 2 2 2^{3} = 2^{2} + 2 ^ {2} 23=22+22
  3. i = 1 i = 1 i=1 开始拿,用ans表示两个插值,因为k降序, 所以再根据第二个条件,对于当前的k[i]如果是加上这个贡献 a n s + = p k [ i ] ans += p^{k[i]} ans+=pk[i], 那么后面肯定要减掉一堆k[j] , a n s − = ∑ j > i p k [ j ] ans -= \sum_{j > i}p^{k[j]} ans=j>ipk[j] ,直到ans = 0 ;
  4. 当ans再次等于0,重复第三个过程
  5. 还有就是ans = 0 , 因为是取了mod之后的,所以这个ans有可能是mod 的倍数,那么要判断ans是否真正为0 , 只需要简单的再搞一个res % Mod , 和 ans % mod 一样 , 只要这两个ans 和 res 同时为 0 , 就表示ans为0,而不是ans 是 mod 的倍数 , 就像一个人说了不算, 两个才有正确性
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7 , Mod = 1e9 + 3;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
ll qmi(ll a , ll b , ll mod)
{
  ll res = 1 ;
  while(b)
   {
     if(b & 1) res = res * a % mod ;
     a = a * a % mod ;
     b >>= 1;
   }
   return res ;
}
ll k[N] ;
void work()
{
  int n = in() , p = in() ;
  for(int i = 1; i <= n ;i ++ ) k[i] = in() ;
  if(p == 1)
   {
     cout << (n % 2) << endl ;
     return ;
   }
  sort(k + 1 , k + n + 1) ;
  reverse(k + 1 ,k + n + 1) ;
  ll res = 0 , ans = 0 ;
  for(int i = 1; i <= n ;i ++ )
     if(!ans && !res) ans += qmi(p , k[i] , mod) , res += qmi(p , k[i] , Mod) ;
     else
       ans = ((ans - qmi(p , k[i] , mod))% mod + mod) % mod ,
       res = ((res - qmi(p , k[i] , Mod))% Mod + Mod) % Mod ;
  cout << ans << endl ;
  return ;

}
int main()
{
  int n = in() ;
  while(n --) work() ;
  return 0 ;
}
/*
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值