luogu P1627 [CQOI2009]中位数
36行或成此题最长题解?
看题先想暴力
暴力~
O(n^2) 赤裸裸地爆炸
考虑优化
重要思想:前缀和
有请前缀和!
我们只要关心相对大小即可:
比m大:赋值为1
比m小:赋值为-1
和m相等:赋值为0
暨求一段奇数个元素的子序列,使它的值的和=0
有请前缀和!
我们知道一个区间总能表示成s[i]-s[j-1],其中s为前缀和数组
我们预处理出前缀和,对前缀的正负性、奇偶性进行分类讨论
答案为所有q[i][0][1]*q[i][1][1]+q[i][0][0]*q[i][1][0]的和
q为计数器
等等!
有个问题:如果不包含m怎么办?
不包含m就变为了偶数区间,不符合,一定不会出现
完美解决!
std:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=100005;
int n,m;
int a[N];//0,-1,1
int s[N]={0};//前缀和
int q[N*2][2][2]={0};//q[i][j][k]为和为i的前缀 长度为奇数/偶数 是否为负数 的出现次数
ll ans=0;
inline ll C2(ll x,ll y){
return x*y;//各选一个
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
if(x>m) a[i]=1;
else if(x==m) a[i]=0;
else a[i]=-1;
}
//一个都没有要添加(假定0为偶数)
q[0][0][0]++;
for(int i=1;i<=n;i++){
s[i]=s[i-1]+a[i];
q[abs(s[i])][i%2][s[i]<0]++; //添加
}
for(int i=0;i<=n;i++){
ans+=(ll)C2(q[i][0][1],q[i][1][1])+(ll)C2(q[i][0][0],q[i][1][0]);//在长度为奇数的区间选一个,偶数的选一个
//cout << i << " " << q[i][0] << " " << q[i][1] << endl;
}
printf("%lld\n",ans);
return 0;
}
//也许在这里有个疑惑:如果不包含m怎么办?
//不包含m就变为了偶数区间,不符合,一定不会出现
完结(记得答案用long long)