Educational Codeforces Round 65 - F - 计数

前言:

这题挺妙的。可能是我对数学计数题熟悉点,想了一会还是搞出来了。个人觉得1900左右。不能有2300的难度。

题目大意:

给你一个序列,问你每一个子数组 [ L , R ] [L,R] [L,R]经排序之后 ∑ i = 1 R − L + 1 a L + i − 1 ∗ i \sum_{i=1}^{R-L+1} a_{L+i-1}*i i=1RL+1aL+i1i的总和.

∑ i = 1 n ∑ j = i n ∑ k = i j a k ∗ ( k − i ) / / k 为 子 数 组 [ i , j ] 经 过 排 序 后 的 下 标 \sum_{i=1}^{n} \sum_{j=i}^{n} \sum_{k=i}^{j} a_{k}*(k-i) //k为子数组[i,j]经过排序后的下标 i=1nj=ink=ijak(ki)//k[i,j]

题目思路:

直接算显然不行,考虑单点的贡献,换更方便的统计方式。老技巧了。

对于第 i i i个位置,值为 a i a_i ai.我们可以将 ≤ a i \leq a_i ai的数都标记成1.反之为0.因为小于等于 a i a_i ai的数会让 a i a_i ai的rank恰好上升1.那么序列大概变成了 0001101011001 这样的感觉.粗体代表 a i a_i ai的位置。

对于经过转变后的01数组,问题转化为求所有覆盖位置 i i i的子数组的区间和的总和。

直接算也显然不行,还是延续上面的老套路,我们继续考虑一个1对整体答案的贡献: i d j ∗ ( n − i + 1 ) id_j*(n-i+1) idj(ni+1). i d j id_j idj代表第j个1的下标位置。

那么对于 i i i左侧的 1 1 1.答案就是 ∑ i d ∗ ( n − i + 1 ) \sum id*(n-i+1) id(ni+1).然后 r e v e r s e reverse reverse一下整个序列,再跑一遍算法即可。

这个过程我们发现我们需要记录一个 ≤ a i \leq a_i ai的数的下标的前缀和。显然树状数组即可。

然后就没了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
ll a[maxn];
ll sum[maxn] , n;
int lowbit(int x){
    
    return x & -x;}
void add(int x,ll c){
    
    
    while(x<=n){
    
    
        sum[x] = (sum[x] + c)%mod;
        x += lowbit(x);
    }
}
ll ask(int x){
    
    
    ll ans=0;
    while(x){
    
    
        ans=(ans + sum[x])%mod;
        x-=lowbit(x);
    }
    return ans;
}
ll dist[maxn] , cnt , c[maxn];
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1 ; i <= n ; i++){
    
    
        cin >> a[i];
        dist[++cnt] = a[i];
    }
    sort(dist + 1 , dist + 1 + cnt);
    cnt = unique(dist + 1 , dist + 1 + cnt) - dist - 1;
    for (int i = 1 ; i <= n ; i++)
        a[i] = lower_bound(dist + 1 , dist + 1 + cnt , a[i]) - dist;
    for (int i = 1 ; i <= n ; i++){
    
    
        add(a[i] , i);
        ll res = ask(a[i]) * (n - i + 1) % mod;
        c[i] = (c[i] + res) % mod;
    }
    reverse(a + 1 , a + 1 + n);
    reverse(c + 1 , c + 1 + n);
    memset(sum , 0 , sizeof sum);
    for (int i = 1 ; i <= n ; i++){
    
    
        ll res = ask(a[i]) * (n - i + 1) % mod;
        c[i] = (c[i] + res) % mod;
        add(a[i] , i);
    }
    ll ans = 0;
    for (int i = 1 ; i <= n ; i++){
    
    
        c[i] = c[i] * dist[a[i]] % mod;
        ans = (ans + c[i]) % mod;
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/114107101