1. 程式人生 > >CF1093:E. Intersection of Permutations(樹狀數組套主席樹)

CF1093:E. Intersection of Permutations(樹狀數組套主席樹)

b數 perm else () fine space span 但是 多少

題意:給定長度為N的a數組,和b數組,a和b都是1到N的排列; 有兩種操作,一種是詢問[L1,R1],[L2,R2];即問a數組的[L1,R1]區間和b數組的[L2,R2]區間出現了多少個相同的數字。 一種是修改b數組兩個位置的值。

思路:如果把b數組每個數取對應a數組對應數的位置,即按照b的下標建立橫坐標,縱坐標是它在a中的位置,那麽問題就成了,詢問在區間[L2,R2]裏面,多少個數在[L1,R1]這區間。 這很明顯就是主席樹可以解決的了。時間復雜度是O(N*logN*logN);常數差不多是4。

因為空間的問題,但是賽場上不少樹套樹都MLE或者RE了。

這裏學到了回收空間:因為這裏的節點不可能出現負數,所以如果一個節點的sum為0,那麽子樹節點的sum值都為0,那麽這個子樹都可以回收,即把它們賦值為0;

(cdq先補了G再回來補。分塊懶得補了。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=200010;
struct in{
    int l,r,sum;
    in(){l=r=sum=0;}
}s[maxn*130];
queue<int>q;
int rt[maxn],pos[maxn],a[maxn],b[maxn],N,cnt;
void add(int &Now,int
L,int R,int pos,int v) { if(!Now){ if(!q.empty()) Now=q.front(),q.pop(); else Now=++cnt; } s[Now].sum+=v; if(L==R) return ;int Mid=(L+R)>>1; if(pos<=Mid) add(s[Now].l,L,Mid,pos,v); else add(s[Now].r,Mid+1,R,pos,v); if(!s[Now].sum) {q.push(Now); Now=0
;} } void Add(int x,int pos,int v) { while(x<=N){ add(rt[x],0,N,pos,v); x+=(-x)&x; } } int query(int Now,int L,int R,int l,int r) { if(L>R) return 0; if(L<0||l<0) return 0; if(!Now) return 0; if(l<=L&&r>=R) return s[Now].sum; int Mid=(L+R)>>1,res=0; if(l<=Mid) res+=query(s[Now].l,L,Mid,l,r); if(r>Mid) res+=query(s[Now].r,Mid+1,R,l,r); return res; } int Query(int x,int L,int R) { if(L>R) return 0; if(x<=0) return 0; int res=0; while(x){ res+=query(rt[x],0,N,L,R); x-=(-x)&x; } return res; } int main() { int M,opt,L1,R1,L2,R2,x,y; scanf("%d%d",&N,&M); rep(i,1,N) scanf("%d",&a[i]),pos[a[i]]=i; rep(i,1,N) scanf("%d",&b[i]); rep(i,1,N) Add(i,pos[b[i]],1); while(M--){ scanf("%d",&opt); if(opt==1){ scanf("%d%d%d%d",&L1,&R1,&L2,&R2); int ans=Query(R2,L1,R1)-Query(L2-1,L1,R1); printf("%d\n",ans); } else { scanf("%d%d",&x,&y); Add(x,pos[b[x]],-1); Add(y,pos[b[y]],-1); swap(b[x],b[y]); Add(x,pos[b[x]],1); Add(y,pos[b[y]],1); } } return 0; }

CF1093:E. Intersection of Permutations(樹狀數組套主席樹)