CF1612G Max Sum Array

解析

被蓝题虐了。(悲
确实不太难,就是没往那边想。

考虑如果某个值的下标分别位 i 1 , i 2 , . . . , i n i_1,i_2,...,i_n i1,i2,...,in 那么如何计算贡献。
每一个下标和前面统计时作为被减数,和后面统计时作为减数,所以 i k i_k ik 的贡献系数就是 ( k − 1 ) − ( n − k ) = 2 k − n − 1 (k-1)-(n-k)=2k-n-1 (k1)(nk)=2kn1
那么所有下标的统计次数其实就是一个公差为 2 的等差数列: 1 − n , 3 − n , . . . , n − 1 1-n,3-n,...,n-1 1n,3n,...,n1
那么现在问题就转化成了,有若干个系数,要求重排使得 ∑ i × a i \sum i\times a_i i×ai 最大。
显然应该让系数升序排列,这也必然符合了每一个数提供的系数必然从前往后递增的限制。
方案数就是有相同系数时其可以产生一个全排列的阶乘。

现在还有一个问题是系数加在一起是 1 0 11 10^{11} 1011 级别的,无法直接统计。
利用等差数列的性质,奇偶分别差分即可。

代码

//luogu
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=2e6+100;
const int inf=1e9;
const int mod=1e9+7;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,k;
int o=1e6+10;
int sum[N];
ll jc[N];
ll niv2=(mod+1)/2;
inline ll S(ll x,ll y){  
  return 1ll*(x+y)%mod*(y-x+1)%mod*niv2%mod;
}
signed main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  n=read();
  for(int i=1;i<=n;i++){
    int x=read();
    sum[1-x+o]++;sum[x-1+o+2]--;
  }
  jc[0]=1;
  for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
  ll val=0,num=1,cnt=0;
  for(int i=2;i<=o+o;i++){
    sum[i]+=sum[i-2];
    num=num*jc[sum[i]]%mod;
    val=(val+S(cnt+1,cnt+sum[i])*((i-o+mod)%mod))%mod;
    cnt+=sum[i];
  }
  printf("%lld %lld\n",val,num);
  return 0;
}
/*
*/
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值