题目大意:
给定一个长度为n的序列a[]以及k,问a[]中有多少个子序列的平均值不小于k
n<=1e5; 1<=a[i],k<=1e4
做题心路历程:
emm,看到这道题目,我当时有点懵逼(这也是CDQ?!),然后我特别疑惑
仔细思考,根据套路先将原序列中的每个数减去一个k,求序列k中有多少子序列之和大于0
然后我也没啥头绪了,就来了个前缀和,想打暴力了,然后静坐五分钟,woc,n^2暴力不是有个这样的式子吗:
if( sum[i] - sum[j] >=0 )total++;( i > j )
昂?WTF?!这不是二维偏序吗?一个偏序对(i,sum[i]),如果存在一个偏序对(j,a[j])使得i > j && sum[i] >=sum[j]不就可以答案+1了吗
哦吼,这道题就没了啊.
思路:
1.首先将原序列中的每个数减去一个k
2.然后求一次前缀和
3.CDQ分治做一个二维偏序(每次统计答案要统计已经插入了多少序列1的数)*(序列1元素的数组下标小于序列二元素的数组下标)
#include <bits/stdc++.h> using namespace std; #define ll long long//这道题一定要开long long,不然是90分 ll n,k; ll a[100005],b[100005],sum[100005],ans=0; ll CDQ(ll l,ll r){ if(l == r)return 0; ll mid=(l+r)>>1; CDQ(l,mid);CDQ(mid+1,r);//递归处理子问题 ll t1=l,t2=mid+1; for (ll i = l ; i <= r ; i ++){ if(sum[t1] <= sum[t2] && t1 <= mid|| t2 > r) b[i]=sum[t1],t1++; else b[i]=sum[t2],t2++,ans+=t1-l;//统计答案,总答案加上已经插入了的序列1的数的个数 } for (ll i = l ; i <= r ; i ++)sum[i]=b[i]; return 0; } int main(){ cin>>n>>k; for (ll i = 1 ; i <= n ; i ++) cin>>a[i],a[i]-=k;//将原序列减去一个k for (ll i = 1 ; i <= n ; i ++) sum[i]=sum[i-1]+a[i];//求一遍前缀和 CDQ(0,n);//来个CDQ求二维偏序 cout<<ans; return 0; }