待
离线
左半部分内部的变化对右半部分没有影响
P3810 三维偏序
题意:
有n个元素,每个元素有a,b,c三种属性,属性的最大值为k
设f(i)为满足a(i)>=a(j),b(i)>=b(j),c(i)>=c(j)的j的数量
对于d属于[0,n),求f(i)=d的 i 的数量
数据范围:n<=1e5,a,b,c,k<=2e5
解法:
先对第一维排序消除第一维影响,然后开始分治,
分治过程中对左右两边按第二维排序,虽然排序之后第一维乱了,但是左半部分的第一维都小于右半部分,
因此只计算左右两部分的偏序关系,第一维乱掉是不会有影响的,计算过程用双指针+树状数组。
详见代码。
同时这题需要去重+合并相同的元素,否则答案会错。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
struct BIT{
int c[maxm];
int lowbit(int i){
return i&-i;
}
void add(int i,int t){
while(i<maxm)c[i]+=t,i+=lowbit(i);
}
int ask(int i){
int ans=0;
while(i)ans+=c[i],i-=lowbit(i);
return ans;
}
}T;
struct Node{
int a,b,c,cnt,ans;
}a[maxm];
int ans[maxm];
int n,k;
bool cmp1(Node a,Node b){
if(a.a!=b.a)return a.a<b.a;
if(a.b!=b.b)return a.b<b.b;
return a.c<b.c;
}
bool cmp2(Node a,Node b){
return a.b<b.b;
}
void cdq(int l,int r){
if(l==r)return ;
int mid=(l+r)/2;
cdq(l,mid);
cdq(mid+1,r);
sort(a+l,a+mid+1,cmp2);
sort(a+mid+1,a+r+1,cmp2);
int j=l;
for(int i=mid+1;i<=r;i++){
while(j<=mid&&a[i].b>=a[j].b){
T.add(a[j].c,a[j].cnt);
j++;
}
a[i].ans+=T.ask(a[i].c);
}
for(int i=l;i<j;i++){//清空树状数组
T.add(a[i].c,-a[i].cnt);
}
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i].a>>a[i].b>>a[i].c;
}
//去重+合并
sort(a+1,a+1+n,cmp1);
int cnt=0;
int num=0;
for(int i=1;i<=n;i++){
cnt++;
if(i==n||a[i].a!=a[i+1].a||a[i].b!=a[i+1].b||a[i].c!=a[i+1].c){
a[++num]={a[i].a,a[i].b,a[i].c,cnt,cnt-1};//当前节点的答案为cnt-1
cnt=0;
}
}
//cdq分治
cdq(1,num);
//
for(int i=1;i<=num;i++){
ans[a[i].ans]+=a[i].cnt;
}
for(int i=0;i<n;i++){
cout<<ans[i]<<endl;
}
return 0;
}
P3157 [CQOI2011]动态逆序对
题意:
给定长度为n的排列,按照某种顺序删除m个元素,
要求计算每次删除之前,整个序列中的逆序对数量
数据范围:n<=1e5,m<=5e4
解法:
先计算出初始逆序对,然后每次计算减少的逆序对数量即可
记录每个元素的权值v,位置pos,删除时间time(不删除的元素time设置为m+1)
对于每一个被删除的元素,减少的逆序对数量为满足下列条件的元素数量:
1.在这个元素前面(pos),权值比他大(v),删除时间比他晚(time)
2.在这个元素后面(pos),权值比他小(v),删除时间比他晚(time)
这是一个三维偏序问题,那么可以用cdq分治解决。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
struct BIT{
int c[maxm];
int lowbit(int i){
return i&-i;
}
void add(int i,int t){
while(i<maxm)c[i]+=t,i+=lowbit(i);
}
int ask(int i){
int ans=0;
while(i)ans+=c[i],i-=lowbit(i);
return ans;
}
}T;
struct Node{
int v,pos,time,ans=0;
//权值,位置,删除时间
}a[maxm];
int pos[maxm];
int rev[maxm];
int n,m;
bool cmp1(Node a,Node b){//这题用不到,因为初始情况pos已经有序
return a.pos<b.pos;
}
bool cmp2(Node a,Node b){
return a.v>b.v;
}
void cdq(int l,int r){
if(l==r)return ;
int mid=(l+r)/2;
cdq(l,mid);
cdq(mid+1,r);
//权值从大到小排序
sort(a+l,a+mid+1,cmp2);
sort(a+mid+1,a+r+1,cmp2);
//
int j=l;
for(int i=mid+1;i<=r;i++){
while(j<=mid&&a[j].v>a[i].v){
T.add(a[j].time,1);
j++;
}
a[i].ans+=(j-l)-T.ask(a[i].time);
}
for(int i=l;i<j;i++){//清空树状数组
T.add(a[i].time,-1);
}
//
j=r;
for(int i=mid;i>=l;i--){
while(j>=mid+1&&a[j].v<a[i].v){
T.add(a[j].time,1);
j--;
}
a[i].ans+=(r-j)-T.ask(a[i].time);
}
for(int i=r;i>j;i--){//清空树状数组
T.add(a[i].time,-1);
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].v;
a[i].pos=i;
a[i].time=m+1;
pos[a[i].v]=i;
}
//计算初始逆序对
int ans=0;
for(int i=1;i<=n;i++){
T.add(a[i].v,1);
ans+=i-T.ask(a[i].v);
}
for(int i=1;i<=n;i++){//清空树状数组
T.add(a[i].v,-1);
}
//
for(int i=1;i<=m;i++){
int x;cin>>x;
a[pos[x]].time=i;
}
//初始情况pos已经有序了
cdq(1,n);
//
for(int i=1;i<=n;i++){
rev[a[i].time]+=a[i].ans;
}
for(int i=1;i<=m;i++){
cout<<ans<<endl;
ans-=rev[i];
}
return 0;
}