CQOI2011 动态逆序对
题目链接:戳我
其实和lrj蓝书上面一个题一模一样呢QAQ
回来再补树套树的写法吧,这里就先放上cdq分治——
我们可以先统计出总共的,然后再对每一步统计删去的逆序对个数。
考虑删去一个数会影响哪些呢,首先肯定时间戳要比它小qwq(这样才能保证应该在它前面的操作都进行了),然后因为cdq分治是只有一边对另一边的贡献,所以要分类讨论:
- \(pos_i<pos_j\)&&\(val_i>val_j\)
- \(pos_i>pos_j\)&&\(val_i<val_j\)
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
#define int long long
using namespace std;
int n,m,cnt;
int tree[MAXN<<2],pos[MAXN],ans[MAXN];
struct Node{int ti,pos,val,op;}node[MAXN<<1];
inline bool cmp(struct Node x,struct Node y){return x.pos<y.pos;}
inline void add(int x,int k)
{
for(int i=x;i<=n;i+=i&(-i))
tree[i]+=k;
}
inline int query(int x)
{
int cur_ans=0;
for(int i=x;i;i-=i&(-i))
cur_ans+=tree[i];
return cur_ans;
}
inline void clear(int x)
{
for(int i=x;i<=n;i+=i&(-i))
tree[i]=0;
}
inline void cdq(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
sort(&node[l],&node[mid+1],cmp);
sort(&node[mid+1],&node[r+1],cmp);
int i=l;
for(int j=mid+1;j<=r;j++)
{
while(i<=mid&&node[i].pos<=node[j].pos) add(node[i].val,node[i].op),i++;
ans[node[j].ti]+=node[j].op*(query(n)-query(node[j].val));
}
for(int j=l;j<i;j++) clear(node[j].val);
i=mid;
for(int j=r;j>=mid+1;j--)
{
while(i>=l&&node[i].pos>=node[j].pos) add(node[i].val,node[i].op),i--;
ans[node[j].ti]+=node[j].op*(query(node[j].val-1));
}
for(int j=mid;j>i;j--) clear(node[j].val);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
int x;
scanf("%lld",&x);
pos[x]=i;
node[++cnt]=(Node){0,i,x,1};
}
for(int i=1;i<=m;i++)
{
int x;
scanf("%lld",&x);
node[++cnt]=(Node){i,pos[x],x,-1};
}
cdq(1,cnt);
for(int i=1;i<=m;i++) ans[i]+=ans[i-1];
for(int i=0;i<m;i++) printf("%lld\n",ans[i]);
return 0;
}