掌中之物,未必在掌握之中。|

2021hych

园龄:2年7个月粉丝:2关注:2

P10149 [Ynoi1999] XM66F题解

image

题解

首先,问题是静态的区间查询问题,一眼莫队。那么我们就需要考虑所需要维护的内容在区间扩增或者缩减时的变化如何快速维护。我们可以先写出对于区间 [l,r] 来说,满足 li<j<kr 的有序三元组 (i,j,k) 数量的表达式,方便拆式子:

i=lrj=li1[aj=ai]k=j+1i1[ak<ai]

记作形式 1。或者:

i=lrj=i+1r[aj=ai]k=i+1j1[ak<ai]

记作形式 2,上述括号均为艾弗森括号。

接下来我们分别考虑 [l,r] 扩增为 [l,r+1][l1,r] 的情况,至于缩减,作为扩增的逆操作是同理的,先看右端点右移,运用形式 1,增量为:

j=lr[aj=ar+1]k=j+1r[ak<ar+1]

注意到后面的求和式实际上是一个二维偏序,我们按照前缀和的思路记:

cnti=j=1i[aj<ai]

显然可以用树状数组做到 O(nlogn) 的预处理。那么原来的增量就能写成:

cntr+1j=lr[aj=ar+1]j=lrcntj[aj=ar+1]

这下就很显然了,我们动态维护两个桶 s1s2,其中下标为 ar+1s1=j=lr[aj=ar+1]s2=j=lrcntj[aj=ar+1]。扩增后,只需要对该下标的 s1 增加 1,s2 增加 cntr+1,转移是 O(1) 的。

对于左端点左移,我们运用形式 2,和刚才一样的方法,增量可以写成:

cntl1j=lr[aj=al1]+j=lrcntj[aj=al1]

形式一点没变,但是系数变为原来的相反数,这不影响统计,我们仍然可以按照刚才的维护方法进行左端点左移,不过我们可能要特殊判断一下增量的系数。

但也不必,显然区间扩增后,增量一定是非负数,那么我们可以认为左端点左移的增量式子是其相反数的绝对值,而右端点右移的增量式子是其本身的绝对值,这样形式和系数都统一了,不需要特判,代码实现比较工整。

由于可以 O(1) 转移,复杂度为 O(nn),加上奇偶排序的话常数就不是很大了,可以通过本题。

AC code

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=5e5+10;
int n,m,a[N],l,r,cnt[N],s1[N],s2[N]; 
int B,ans[N],now;
struct Query {
	int l,r,id,bel;
}q[N];
struct BST {
	int c[N]; 
	int lowbit(int x) {
		return x&-x;
	}
	void update(int x,int y) {
		for(;x<=n;x+=lowbit(x)) c[x]+=y;
	}
	int sum(int x) {
		int ans=0;
		while(x>0) {
			ans+=c[x];
			x-=lowbit(x);
		}
		return ans;
	}
}T;
bool cmp(Query x,Query y) {
	if(x.bel!=y.bel) return x.bel<y.bel;
	if(x.bel&1) return x.r<y.r;
	else return x.r>y.r;
}
void add(int i) {
	now+=abs(cnt[i]*s1[a[i]]-s2[a[i]]);
	s1[a[i]]++;
	s2[a[i]]+=cnt[i];
}
void del(int i) {
	s1[a[i]]--;
	s2[a[i]]-=cnt[i];
	now-=abs(cnt[i]*s1[a[i]]-s2[a[i]]);
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	B=(int)(n*1.0/sqrt(m));
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) {
		T.update(a[i],1);
		cnt[i]=T.sum(a[i]-1);
	}
	for(int i=1;i<=m;i++) {
		cin>>l>>r;
		q[i]={l,r,i,(l-1)/B+1};
	}
	sort(q+1,q+m+1,cmp);
	l=1,r=1;
	s1[a[1]]++;
	s2[a[1]]+=cnt[1]; 
	for(int i=1;i<=m;i++) {
		while(l>q[i].l) add(--l);
		while(r<q[i].r) add(++r);
		while(l<q[i].l) del(l++);
		while(r>q[i].r) del(r--);
		ans[q[i].id]=now;
	}
	for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
	return 0;
}

本文作者:2021hych

本文链接:https://www.cnblogs.com/2021hych/p/18017402

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   2021hych  阅读(29)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起