Codeforces 785E 题解(树套树-树状数组套线段树)

题目大意:

对于一个长度为n的序列进行k次操作,每次操作都是交换序列中的某两个数。对于每一个操作,回答当前序列中有多少个逆序对。


题解:

每次更改序列都可以理解为,将答案减去被调换的位置原有数字的对答案的贡献,然后调换两数字的位置,然后将答案加上被调换的位置在调换之后的数字对答案的贡献。

每一个数字对答案的贡献都可以理解为在其出现位置之前的比它大的数字的个数加上在其出现位置之后的比它小的数字的个数。

由此,用树状数组套线段树可做。树状数组是根据位置维护的,树状数组的每个结点包含一个权值线段树,但是直接开权值线段树的话空间会爆,所以线段数要动态开点,那么每一次修改只会至多使用log2n的空间。

同时也要注意,计算贡献的时候要注意被调换的两个数字之间的相互影响。


代码:

树套树版本
#include <cstdio>
#include <iostream>
using namespace std;

#define lowbit(k) (k&-(k))
const int maxn=int(2e5)+111;
int n,m;

struct Node {
    int sum,ls,rs;
    Node() {}
    Node(int s,int l,int r):sum(s),ls(l),rs(r) {}
}node[maxn*200];

int root[maxn],tot=0;

void seg_modify(int k,int l,int r,int pos,int val) {
    if(l==r && l==pos) {
        node[k].sum=val;
        return;
    }
    int mid=(l+r)>>1;
    int &ls=node[k].ls,&rs=node[k].rs;
    if(pos<=mid) {
        if(!ls) ls=++tot;
        seg_modify(ls,l,mid,pos,val);
    }
    else {
        if(!rs) rs=++tot;
        seg_modify(rs,mid+1,r,pos,val);
    }
    node[k].sum=(ls?node[ls].sum:0)+(rs?node[rs].sum:0);
    return;
}

int seg_query(int k,int l,int r,int ql,int qr) {
    if(ql<=l && r<=qr) return node[k].sum;
    int mid=(l+r)>>1;
    int &ls=node[k].ls,&rs=node[k].rs;
    if(qr<=mid) return ls?seg_query(ls,l,mid,ql,qr):0;
    if(ql> mid) return rs?seg_query(rs,mid+1,r,ql,qr):0;
    return (ls?seg_query(ls,l,mid,ql,qr):0)+(rs?seg_query(rs,mid+1,r,ql,qr):0);
}

void bit_build() {
    for(int i=1;i<=n;i++) root[i]=i;
    tot=n;
    for(int i=1;i<=n;i++) {
        int len=lowbit(i);
        for(int j=i;j>=(i-len+1);j--) seg_modify(root[i],1,n,j,1);
    }
    return;
}

int bit_query(int pos,int l,int r) {
    int res=0;
    while(pos) {
        res+=seg_query(root[pos],1,n,l,r);
        pos-=lowbit(pos);
    }
    return res;
}

void bit_modify(int pos,int val,int dir) {
    while(pos<=n) {
        seg_modify(root[pos],1,n,val,dir);
        pos+=lowbit(pos);
    }
    return;
}

int a[maxn];

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE

    scanf("%d%d",&n,&m);
    bit_build();

    long long ans=0;
    for(int i=1;i<=n;i++) a[i]=i;

    while(m--) {
        int x,y;
        scanf("%d%d",&x,&y);
        int g1=bit_query(n,1,a[x]-1)-bit_query(x,1,a[x]-1)+bit_query(x-1,a[x]+1,n);
        bit_modify(x,a[x],0);
        int g2=bit_query(n,1,a[y]-1)-bit_query(y,1,a[y]-1)+bit_query(y-1,a[y]+1,n);
        bit_modify(y,a[y],0);
        ans-=g1+g2;
        swap(a[x],a[y]);

        bit_modify(x,a[x],1);
        int c1=bit_query(n,1,a[x]-1)-bit_query(x,1,a[x]-1)+bit_query(x-1,a[x]+1,n);
        bit_modify(y,a[y],1);
        int c2=bit_query(n,1,a[y]-1)-bit_query(y,1,a[y]-1)+bit_query(y-1,a[y]+1,n);
        ans+=c1+c2;
        printf("%I64d\n",ans);
    }

    return 0;
}

分块套树状数组版本(lewin)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
// taken from here: https://github.com/igrsk/spoj/blob/master/SWAPS.cpp
#define FORO(i,n) for(int i = 0;i < n;i++)
#define FORI(i,n) for(int i = 1;i <= n;i++) 
const int MAXN = 200000;
const int MAXA = 200000;
const int MAXSQRTN = 500;

int A[MAXN];
int N, M;
int bit[MAXSQRTN+1][MAXA+1];
int sqrtN;

void bitinc(int i,int v,int *d) {
  for(;i <= MAXA;i += i&-i) d[i]+=v;
}

void bitins(int x,int y,int v) {
  while(x <= sqrtN) {
    bitinc(y,v,bit[x]);
    x += x&-x;
  }
}

int bitsum(int x,int y) {
  int ret = 0;
  for(;x > 0;x-=x&-x)
    for(int yy = y;yy > 0;yy-=yy&-yy)
      ret += bit[x][yy];
  return ret;
}

void init() {
  for(sqrtN = 1;sqrtN*sqrtN < N;sqrtN++) ;
  for(int i = 0;i < N;i++) {
    bitins(i/sqrtN+1,A[i],1);
  }
}

int query(int i,int x) {
  int ret = bitsum(i/sqrtN,x);
  // 0 ~ sqrtN-1, sqrtN ~ 2*sqrtN-1, ... 
  for(int j = sqrtN*(i/sqrtN);j <= i;j++)
    if(A[j] <= x) ret++;
  return ret;
}

int main() {
  freopen("input.txt","r",stdin);
  freopen("output.txt","w",stdout);

  scanf("%d %d",&N, &M);
  FORO(i,N) A[i] = i+1;

  init();

  long long orans = 0;
  FORO(i,N) {
    orans += i-query(i,A[i])+1;
  }
  cout<<"orans:"<<orans<<endl;

  int X, Y, q1, q2;
  FORO(mm,M) {
    scanf("%d %d",&X,&Y);
    // update and query
    X--; Y--;
    if (X != Y) {
      q1 = A[X];
      q2 = A[Y];


      orans -= (X-query(X-1,A[X])-1)+(query(N-1,A[X]-1)-query(X,A[X]-1));
      bitins(X/sqrtN+1,A[X],-1);

      A[X] = q2;
      bitins(X/sqrtN+1,q2,1);
      orans += (X-query(X-1,q2)-1)+(query(N-1,q2-1)-query(X,q2-1));

      orans -= (Y-query(Y-1,A[Y])-1)+(query(N-1,A[Y]-1)-query(Y,A[Y]-1));
      bitins(Y/sqrtN+1,A[Y],-1);

      A[Y] = q1;
      bitins(Y/sqrtN+1,q1,1);
      orans += (Y-query(Y-1,q1)-1)+(query(N-1,q1-1)-query(Y,q1-1));
    }

    printf("%lld\n",orans);
  }
  return 0;
}

分块套vector版本(kmjp)
#include <bits/stdc++.h>
using namespace std;
typedef signed long long ll;

#undef _P
#define _P(...) (void)printf(__VA_ARGS__)
#define FOR(x,to) for(x=0;x<(to);x++)
#define FORR(x,arr) for(auto& x:arr)
#define ITR(x,c) for(__typeof(c.begin()) x=c.begin();x!=c.end();x++)
#define ALL(a) (a.begin()),(a.end())
#define ZERO(a) memset(a,0,sizeof(a))
#define MINUS(a) memset(a,0xff,sizeof(a))
//-------------------------------------------------------

int N,Q;
int A[201010];
int L,R;
const int D=500;
vector<int> V[500];

void erase(int id,int v) {
    int b=id/D;
    int i;
    FOR(i,V[b].size()) if(V[b][i]==v) {
        V[b].erase(V[b].begin()+i);
        return;
    }
}

void add(int id,int v) {
    int b=id/D;
    int i;
    FOR(i,V[b].size()) if(v<V[b][i]) {
        V[b].insert(V[b].begin()+i,v);
        return;
    }
    V[b].push_back(v);
}

int getmore(int id,int v) {
    int i,j;
    int ret=0;
    FOR(i,500) {
        if(id<(i+1)*D) {
            for(j=i*D;j<id;j++) if(A[j]>v && A[j]!=1<<20) ret++;
            break;
        }
        else {
            ret += V[i].end()-lower_bound(ALL(V[i]),v);
        }
    }
    return ret;
}
int getless(int id,int v) {
    int i,j;
    int ret=0;
    FOR(i,500) {
        if(id<(i+1)*D) {
            for(j=i*D;j<id;j++) if(A[j]<v) ret++;
            break;
        }
        else {
            ret += lower_bound(ALL(V[i]),v)-V[i].begin();
        }
    }
    return ret;
}



void solve() {
    int i,j,k,l,r,x,y; string s;

    cin>>N>>Q;
    FOR(i,N) {
        A[i]=i+1;
        add(i,A[i]);
    }

    ll ret=0;

    while(Q--) {
        cin>>L>>R;
        L--,R--;
        if(L==R) {
            cout<<ret<<endl;
            continue;
        }
        if(L>R) swap(L,R);

        if(A[L]<A[R]) ret--;
        else ret++;

        ret-=getmore(R,A[R]);
        ret-=A[R]-1-getless(R,A[R]);
        ret-=getmore(L,A[L]);
        ret-=A[L]-1-getless(L,A[L]);
        erase(R,A[R]);
        erase(L,A[L]);

        swap(A[L],A[R]);
        add(R,A[R]);
        add(L,A[L]);
        ret+=getmore(R,A[R]);
        ret+=A[R]-1-getless(R,A[R]);
        ret+=getmore(L,A[L]);
        ret+=A[L]-1-getless(L,A[L]);

        cout<<ret<<endl;
    }
}


int main(int argc,char** argv){
    string s;int i;
    if(argc==1) ios::sync_with_stdio(false), cin.tie(0);
    FOR(i,argc-1) s+=argv[i+1],s+='\n';
    FOR(i,s.size()) ungetc(s[s.size()-1-i],stdin);
    solve(); return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值