洛谷 P3658 [USACO17FEB]Why Did the Cow Cross the Road III P(CDQ分治)

传送门


解题思路

对于一个数x,以在第一个排列中的位置作为关键值x,以在第二个排列中的位置作为关键值y,以值本身作为第三个关键值z。

将每个数都用一个三元组(x,y,z)表示出来。

最后答案就是满足 \(x_i<x_j,y_i>y_j,\left\vert {z_i-z_j} \right\vert <k\) 的三元组的个数。

很显然变成了CDQ分治的板子。

其中第三个条件可以用容斥转化为:

\[ans+=query(n)-query(min(a[nowr].z+k,n))+query(max(a[nowr].z-k-1,0)) \]

注意边界。
注意ans开long long。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1e5+5;
struct node{
	int x,y,z;
}a[maxn],b[maxn];
int n,k,d[maxn],qaq[maxn];
long long ans;
bool cmp(node a,node b){
	return a.x<b.x;
}
inline int lowbit(int x){
	return x&(-x);
}
inline void update(int x,int v){
	for(int i=x;i<=n;i+=lowbit(i)){
		d[i]+=v;
	}
}
inline int query(int x){
	int res=0;
	for(int i=x;i>=1;i-=lowbit(i)){
		res+=d[i]; 
	}
	return res;
}
void divide(int l,int r){
	if(l==r) return;
	int mid=(l+r)/2;
	divide(l,mid);
	divide(mid+1,r);
	int now=l,nowl=l,nowr=mid+1;
	queue<int> q;
	while(nowl<=mid||nowr<=r){
		if(nowr<=r&&(nowl>mid||a[nowr].y>a[nowl].y)){
			ans+=query(n)-query(min(a[nowr].z+k,n))+query(max(a[nowr].z-k-1,0));
			b[now++]=a[nowr++];
		}else{
			update(a[nowl].z,1);
			q.push(a[nowl].z);
			b[now++]=a[nowl++];
		}
	}
	for(int i=l;i<=r;i++) a[i]=b[i];
	while(!q.empty()){
		update(q.front(),-1);
		q.pop();
	} 
} 
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		a[i].x=i;
		a[i].z=x;
		qaq[x]=i;
	}
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		a[qaq[x]].y=i;
	}
	divide(1,n);
	cout<<ans;
    return 0;
}
上一篇:莫队阶段小结


下一篇:codeforces#1136E. Nastya Hasn't Written a Legend(二分+线段树)