D 区间求和 [数学 树状数组]

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();
}

posted @ 2017-05-01 21:49  Candy?  阅读(286)  评论(0编辑  收藏  举报