luogu P1627 [CQOI2009]中位数

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)

猜你喜欢

转载自blog.csdn.net/weixin_43872370/article/details/84728413