D 區間求和 [數學 樹狀陣列]

Candy?發表於2017-05-01

D 區間求和

題意:求
\[ \sum_{k=1}^n \sum_{l=1}^{n-k+1} \sum_{r=l+k-1}^n 區間前k大值和 \]


比賽時因為被B卡了沒有深入想這道題 結果B沒做出來後面的題也沒做

化一下式子
\[ \begin{align} &= \sum_{l=1}^n \sum_{r=l}^n \sum_{k=l}^r a_k \cdot (1+\sum_{i=l}^r [a_i < a_k]) \\ &考慮一個數的貢獻 \\ &= \sum_{k=1}^n \sum_{i=k+1}^n a_k \cdot [a_i < a_k] \cdot k \cdot (n-i+1)\\ &+ \sum_{k=1}^n \sum_{i=1}^{k-1} a_k \cdot [a_i < a_k] \cdot i \cdot (n-k+1) \\ &+ \sum_{k=1}^n a_k \cdot k \cdot (n-k+1) \end{align} \]
簡單的二維偏序問題,樹狀陣列搞一下就行了

注意數相等的情況!第二個二維偏序把相等認為是大於就行了

一定要考慮這種做題方法:

把一些最大值、最小值、k大值之類的關係用求和式子表示出來進行化簡

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e6+5, mo = 1e9+7;
inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, a[N], mp[N]; ll A, B, C;

ll c[N];
inline void mod(ll &x) {if(x >= mo) x -= mo; else if(x < 0) x += mo;}
inline void add(int p, ll v) {for(; p<=n; p+=p&-p) mod(c[p] += v);}
inline ll sum(int p) {ll ans=0; for(; p; p-=p&-p) mod(ans += c[p]); return ans;}
void solve() {
    ll ans = 0;
    for(int k=n; k>=1; k--) mod(ans += (ll) mp[a[k]] * k %mo * sum(a[k]) %mo), add(a[k], (n-k+1));
    memset(c, 0, sizeof(c));
    for(int k=1; k<=n; k++) mod(ans += (ll) mp[a[k]] * (n-k+1) %mo * sum(a[k]-1) %mo), add(a[k], k);
    for(int k=1; k<=n; k++) mod(ans += (ll) mp[a[k]] * k %mo * (n-k+1) %mo);
    printf("%lld\n", (ans + mo) %mo);
}
int main() {
    freopen("in", "r", stdin);
    n=read(); a[1]=read(); A=read(); B=read(); C=read();
    for(int i=2; i<=n; i++) a[i] = (a[i-1] * A + B) % C;
    for(int i=1; i<=n; i++) mp[i] = a[i];
    sort(mp+1, mp+1+n); mp[0] = unique(mp+1, mp+1+n) - mp - 1;
    for(int i=1; i<=n; i++) a[i] = lower_bound(mp+1, mp+1+mp[0], a[i]) - mp;
    solve();
}

相關文章