题目大意:一个给定非负数组,每次操作替换其中一个数字,问替换后满足ai+aj<=w的数对有多少个(i<=j)?
先来个基础问题:一个数组中满足ai+aj<=w的数对有多少个? 一种方案:先排序+双指针遍历,如果a[1]最小(指针1),那么找到最大的a[i](指针2),使得a[1]+a[i]<=w,而a[1]+a[i+1]>w。那么显然a1能和这i个元素满足条件。然后两个指针同时后移即可。
本题目思路:注意题目给出数据范围ai一定小于300000。如果我们随意去除一个值为x的元素,那么会减少多少个满足条件的数对呢?显然这个值等于数组中小于等于w-x的元素个数。如果添加一个y进来,那么数组中所有小于等于w-y的元素都能和y凑成新数对。如何快速求出一个数组中小于某个值的元素个数,且数组中元素可变?
显然此问题是数据结构中经典的“树状数组”问题。具体过程见代码.
#include <iostream>
using namespace std;
int n,q,w,t[300005],a[300005];
long long all=0;
void add(int x,int v)
{
for(;x<=300005;x+=x&(-x);)
t[x]+=v;
}
int sum(int x)
{
int s=0;
for(; x>0; x-=x&(-x))
s+=t[x];
return s;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,p,x;/**< 树状数组不能解决0的问题,所以数据都加1处理,w则+2处理 */
cin>>n>>q>>w;
w+=2;
for(i=1; i<=n; i++)
{
cin>>a[i],a[i]++,add(a[i],1);/**< 加入树状数组 */
all+=sum(w-a[i]);/**< 求a[i]能凑成符合条件数对个数 */
}
while(q--)
{
cin>>p>>x;
x++;
all-=sum(w-a[p]);/**< 因为要把a[p]替换为x,所以先去除a[p]的数对 */
add(a[p],-1);/**< 去除a[p] */
a[p]=x;
add(x,1);/**< 替换a[p]值为x,同时加入树状数组 */
all+=sum(w-x);/**< 新增x带来的数对 */
cout<<all<<endl;
}
return 0;
}