2018牛客网暑期ACM多校训练营(第一场)F:Sum of Maximum
题意:给定N个数a[],现在用a形成一个新的数组b[],1<=b[i]<=a[i]。 问所有的方案的最大值之和。
思路:先排序。然后分段统计贡献,假设a[i-1]<a[i],那么[a[i-1]+1,a[i]]的贡献就是左边的所有方案*右边的合法方案,合法即是最大值这个区间内。
假设max=x,那么右边的贡献是x*(x^(n-i+1)-(x-1)*(n-i+1)); 所有的x加起来,发现是个前缀和,=x^(n-i+2)+(x-1)^(n-i+1)+...^(n-i+1);最右边部分可以用拉格朗日求出。
所有就完了。 但是我的板子好像有点慢。
(毕竟我多加了一个log,明天来修改一下。今天还有几个题要补。
(实则是在准备板子。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; const int Mod=1e9+7; const int mod=1e9+7; int a[maxn],sum[maxn],ans; ll f[maxn],fac[maxn],inv[maxn]; ll P(ll a,ll b) { ll ans=1; while(b) { if(b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } if(ans<0) ans+=mod; return ans; } void init(int tot) { fac[0]=1; for(int i=1;i<=tot;i++) fac[i]=fac[i-1]*i%mod; inv[tot]=P(fac[tot],mod-2); inv[0]=1; //求阶乘逆元 for(int i=tot-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod; } ll Lagrange(ll n,ll k) { rep(i,1,k+1) f[i]=(f[i-1]+P(i,k-1))%mod; if(n<=k+1) return f[n]; int tot=k+1; init(tot); ll ans=0,now=1; for(int i=1;i<=tot;i++) now=now*(n-i)%mod; for(int i=1;i<=tot;i++) { ll inv1=P(n-i,mod-2); ll inv2=inv[i-1]*inv[tot-i]%mod; if((tot-i)&1) inv2=mod-inv2; ll temp=now*inv1%mod; temp=temp*f[i]%mod*inv2%mod; ans+=temp; if(ans>=mod) ans-=mod; } return ans; } int solve(int x,int k) { if(!x) return 0; return P(x,k+1)-Lagrange(x-1,k+1); } int main() { int N; while(~scanf("%d",&N)){ rep(i,1,N) scanf("%d",&a[i]); sort(a+1,a+N+1); ans=0; sum[0]=1; rep(i,1,N) sum[i]=1LL*sum[i-1]*a[i]%Mod; rep(i,1,N) { if(a[i]==a[i-1]) continue; int res=(solve(a[i],N-i+1)-solve(a[i-1],N-i+1)+Mod)%Mod; (ans+=1LL*sum[i-1]*res%Mod)%=Mod; } printf("%d\n",ans); } return 0; }
It is your time to fight!