牛客多校Round1 F Sum of Maximum

%%%

参考自:http://tokitsukaze.live/2018/07/19/2018niuke1.F/
题目链接 : https://www.nowcoder.com/acm/contest/139/F

sol:

写一个基于伯努利数的做法

首先先对 a i a_i ai排序,将x分割成 ( 0 , a 1 ] (0,a1] (0,a1], ( a 1 , a 2 ] (a1,a2] (a1,a2], ⋯ \cdots , ( a n − 1 , a n ] (a_{n-1},a_n] (an1,an]这样的n个区间,统计 x x x在每个区间时的贡献。

对于最大值 x ∈ ( a i − 1 , a i ] x \in(a_{i-1},a_i] x(ai1,ai]

  • x 1 到 x i − 1 x_1 到 x_{i-1} x1xi1可以任取,贡献为 ∏ j = 1 i − 1 a j \prod_{j=1}^{i-1}a_j j=1i1aj,记为 n o w now now
  • x i 到 x n x_i 到 x_n xixn必须存在一个 x j x_j xj x x x且所有数小于等于 x x x。由容斥定理可知,答案为所有可能的减去不合法的方案(全部都小于 x x x),答案为 x n − i + 1 − ( x − 1 ) n − i + 1 x^{n-i+1} - (x-1)^{n-i+1} xni+1(x1)ni+1,记为 f ( x ) f(x) f(x)
  • x x x的贡献为 n o w ∗ f ( x ) ∗ x now * f(x) *x nowf(x)x

那么对于 x ∈ ( a i − 1 , a i ] x\in(a_{i-1},a_i] x(ai1,ai],贡献为 ∑ a i − 1 + 1 a i n o w ∗ f ( x ) ∗ x \sum_{a_{i-1}+1}^{a_i} now * f(x) * x ai1+1ainowf(x)x

定义 g ( a i ) = ∑ x = 1 a i x ∗ ( x n − i + 1 − ( x − 1 ) n − i + 1 ) g(a_i) =\sum_{x=1}^{a_i}x * (x^{n-i+1} -(x-1)^{n-i+1}) g(ai)=x=1aix(xni+1(x1)ni+1)
推一下式子:
g ( a i ) = ∑ x = 1 a i x ∗ ( x n − i + 1 − ( x − 1 ) n − i + 1 ) = ∑ x = 1 a i x n − i + 2 − ∑ x = 1 a i ( x − 1 + 1 ) ( x − 1 ) n − i + 1 ) = ∑ x = 1 a i x n − i + 2 − ∑ x = 0 a i − 1 x n − i + 2 − ∑ x = 0 a i − 1 x n − i + 1 = x n − i + 2 − ∑ x = 1 a i − 1 x n − i + 1 \begin{aligned} g(a_i) &= \sum_{x=1}^{a_i}x * (x^{n-i+1} -(x-1)^{n-i+1}) \\ &= \sum_{x=1}^{a_i}x^{n-i+2} -\sum_{x=1}^{a_i}(x-1 +1)(x-1)^{n-i+1})\\ &= \sum_{x=1}^{a_i}x^{n-i+2} - \sum_{x=0}^{a_i-1}x^{n-i+2} - \sum_{x=0}^{a_i -1}x^{n-i+1}\\ &= x^{n-i+2} - \sum_{x=1}^{a_i - 1} x^{n-i+1} \end{aligned} g(ai)=x=1aix(xni+1(x1)ni+1)=x=1aixni+2x=1ai(x1+1)(x1)ni+1)=x=1aixni+2x=0ai1xni+2x=0ai1xni+1=xni+2x=1ai1xni+1
这一段贡献就是 g ( a i ) − g ( a i − 1 ) g(a_i) - g(a_{i-1}) g(ai)g(ai1),记为 r e s i res_i resi,发现右边是个自然数幂和的形式,可以用 O ( n 2 ) O(n^2) O(n2)的做法预处理伯努利数后, O ( n ) O(n) O(n)算出来。

总的答案就是 a n s = ∑ i = 1 n r e s i ans = \sum_{i=1}^{n} res_i ans=i=1nresi

code:

#include 
#include 
#include 
#include 
#include 
 
using namespace std;
typedef long long ll;
 
const int maxn = 2e3+10;
const int mod = 1e9+7;
 
ll inv[maxn],fac[maxn],iinv[maxn];
ll B[maxn];
 
ll qpow(ll a,ll b){
    a %= mod;
    ll ret = 1;
    while(b){
        if(b&1) ret = ret * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ret;
}
 
ll Inv(ll n){
    return qpow(n,mod-2);
}
 
void Add(ll& x,ll y){
    x += y;
    if(x>=mod) x -= mod;
}
 
void Mul(ll& x,ll y){
    x *= y;
    if(x>=mod) x %=mod;
}
 
inline ll C(int n, int m) {
    return fac[n] * iinv[n - m] % mod*iinv[m] % mod;
}
 
void init() {
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    iinv[0] = iinv[1] = 1;
    for(int i = 2; i <maxn; i++) fac[i] = fac[i - 1] * i %mod;
    for(int i = 2; i <maxn; i++) inv[i] = inv[mod % i] * (mod - mod/i) % mod;
    for(int i = 2; i <maxn; i++) iinv[i] = iinv[i-1] * inv[i] % mod;
    B[0] = 1;
    for(int i = 1;i<2005;i++){
        B[i] = 0;
        for(int j = 0;j<i;j++){
            Add(B[i],C(i+1,j) * B[j] % mod);
        }
        Mul(B[i],inv[i+1]);
        B[i] = -B[i];
        Add(B[i],mod);
    }
}
 
 
ll a[maxn];
 
inline ll sol(ll n,ll k){
    ll ans = 0;
    ll tmp = n + 1;
    for(int i = 1;i<=k+1;i++){
        ll ret = C(k+1,i);
        Mul(ret,B[k+1-i]);
        Mul(ret,tmp);
        Add(ans,ret);
        Mul(tmp,n+1);
    }
    Mul(ans,inv[k+1]);
    return ans;
}
 
int main(){
    init();
    int n;
    while(scanf("%d",&n)==1){
        for(int i = 1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        ll ans = 0;
        ll mul = 1;
        for(int i = 1;i<=n;i++){
            if(a[i] == a[i-1]){
                Mul(mul,a[i]);
                continue;
            }
            ll ret2 = (qpow(a[i],n-i + 2) - sol(a[i]-1,n-i+1) + mod) % mod;
            ll ret1 = (qpow(a[i-1],n-i+2) - sol(a[i-1]-1,n-i+1) + mod) % mod;
            Add(ret2,mod - ret1);
            Mul(ret2,mul);
            Add(ans,ret2);
            Mul(mul,a[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

再放一个 d l s dls dls拉格朗日插值的做法, d l s dls dls的板子好像是线性的?

#include 
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define mem(a,b) memset((a),(b),sizeof(a))
#define MP make_pair
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define all(x) x.begin(),x.end()
#define _GLIBCXX_PERMIT_BACKWARD_HASH
#include 
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef vector<int> VI;
typedef vector<ll> VL;
struct str_hash{size_t operator()(const string& str)const{return __stl_hash_string(str.c_str());}};
const int INF=0x3f3f3f3f;
const ll LLINF=0x3f3f3f3f3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-4;
const int MAX=2e5+10;
const ll mod=1e9+7;
 
/****************************************  head  ****************************************/
 
namespace polysum {
    #define rep(i,a,n) for (int i=a;i
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    const int D=101000;
    ll a[D],tmp[D],f[D],g[D],p[D],p1[D],p2[D],b[D],h[D][2],C[D];
    ll powmod(ll a,ll b){ll res=1;a%=mod;assert(b>=0);for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    ll calcn(int d,ll *a,ll n) { // a[0].. a[d]  a[n]
        if (n<=d) return a[n];
        p1[0]=p2[0]=1;
        rep(i,0,d+1) {
            ll t=(n-i+mod)%mod;
            p1[i+1]=p1[i]*t%mod;
        }
        rep(i,0,d+1) {
            ll t=(n-d+i+mod)%mod;
            p2[i+1]=p2[i]*t%mod;
        }
        ll ans=0;
        rep(i,0,d+1) {
            ll t=g[i]*g[d-i]%mod*p1[i]%mod*p2[d-i]%mod*a[i]%mod;
            if ((d-i)&1) ans=(ans-t+mod)%mod;
            else ans=(ans+t)%mod;
        }
        return ans;
    }
    void init(int M) {
        f[0]=f[1]=g[0]=g[1]=1;
        rep(i,2,M+5) f[i]=f[i-1]*i%mod;
        g[M+4]=powmod(f[M+4],mod-2);
        per(i,1,M+4) g[i]=g[i+1]*(i+1)%mod;
    }
    ll polysum(ll n,ll *a,ll m) { // a[0].. a[m] \sum_{i=0}^{n-1} a[i]
        rep(i,0,m+1) tmp[i]=a[i];
        tmp[m+1]=calcn(m,tmp,m+1);
        rep(i,1,m+2) tmp[i]=(tmp[i-1]+tmp[i])%mod;
        return calcn(m+1,tmp,n-1);
    }
    ll qpolysum(ll R,ll n,ll *a,ll m) { // a[0].. a[m] \sum_{i=0}^{n-1} a[i]*R^i
        if (R==1) return polysum(n,a,m);
        a[m+1]=calcn(m,a,m+1);
        ll r=powmod(R,mod-2),p3=0,p4=0,c,ans;
        h[0][0]=0;h[0][1]=1;
        rep(i,1,m+2) {
            h[i][0]=(h[i-1][0]+a[i-1])*r%mod;
            h[i][1]=h[i-1][1]*r%mod;
        }
        rep(i,0,m+2) {
            ll t=g[i]*g[m+1-i]%mod;
            if (i&1) p3=((p3-h[i][0]*t)%mod+mod)%mod,p4=((p4-h[i][1]*t)%mod+mod)%mod;
            else p3=(p3+h[i][0]*t)%mod,p4=(p4+h[i][1]*t)%mod;
        }
        c=powmod(p4,mod-2)*(mod-p3)%mod;
        rep(i,0,m+2) h[i][0]=(h[i][0]+h[i][1]*c)%mod;
        rep(i,0,m+2) C[i]=h[i][0];
        ans=(calcn(m,C,n)*powmod(R,n)-c)%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
}
 
const int maxn = 1e3 + 10;
 
ll qpow(ll _a,int _b){
    ll ret = 1;
    while(_b){
        if(_b&1) ret = ret * _a % mod;
        _a = _a * _a % mod;
        _b >>= 1;
    }
    return ret;
}
 
int main(){
    int n;
    polysum::init(1100);
    ll a[maxn],b[maxn];
    while(scanf("%d",&n)==1){
        for(int i = 1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        ll ans = 0;
        ll Mul = 1;
        for(int i = 1;i<=n;i++){
            if(a[i] == a[i-1]){
                Mul = Mul * a[i] % mod;
                continue;
            }
            b[0] = 0;
            for(int j = 1;j<=n-i+1;j++)
                b[j] = (qpow(j,n+1-i) + mod - qpow(j-1,n-i+1)) * j % mod;
            ll ret = polysum::polysum(a[i]+1,b,n-i+1) + mod - polysum::polysum(a[i-1] + 1,b,n-i+1);
            ans += ret * Mul % mod;
            Mul = Mul * a[i] % mod;
        }
        ans %= mod;
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(拉格朗日插值,伯努利数)