【Bzoj 3295】 动态逆序对(树套树|CDQ分治)
【题意】
每次删除一个数,然后问删除前逆序对数。
【分析】
没有AC不开心。。
我的树状数组套字母树,应该是爆空间的,空间复杂度O(nlogn^2)啊。。哭。。
然后就没有然后了,别人家的树套树是树状数组套平衡树,O(nlogn)的啊。。
别人家的CDQ分治更屌。。我垃圾咯。
只是存个代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 200010 9 #define Maxd 31 10 11 int a[Maxn],wr[Maxn]; 12 int n,m; 13 14 struct node 15 { 16 int son[2],cnt; 17 }tr[Maxn*10*20];int tot; 18 19 void upd(int x) 20 { 21 tr[x].son[0]=tr[x].son[1]=0; 22 tr[x].cnt=0; 23 } 24 25 void add(int now,int y,int c) 26 { 27 for(int i=Maxd;i>=1;i--) 28 { 29 int ind=y>>i-1; 30 y=y%(1<<i-1); 31 if(!tr[now].son[ind]) 32 { 33 tr[now].son[ind]=++tot; 34 upd(tot); 35 } 36 now=tr[now].son[ind]; 37 tr[now].cnt+=c; 38 } 39 } 40 41 int ffind(int now,int y) 42 { 43 int ans=0; 44 for(int i=Maxd;i>=1;i--) 45 { 46 int ind=y>>i-1; 47 y=y%(1<<i-1); 48 if(ind==0) 49 { 50 ans+=tr[tr[now].son[1]].cnt; 51 now=tr[now].son[0]; 52 } 53 else now=tr[now].son[1]; 54 } 55 return ans; 56 } 57 58 int sm[Maxn]; 59 void change(int x,int y,int c) 60 { 61 for(int i=x;i<=n;i+=i&(-i)) 62 add(i,y,c),sm[i]+=c; 63 64 } 65 66 int ad; 67 int query(int x,int y) 68 { 69 ad=0; 70 int ans=0; 71 for(int i=x;i>=1;i-=i&(-i)) 72 ad+=sm[i],ans+=ffind(i,y); 73 return ans; 74 } 75 76 int main() 77 { 78 int ans=0; 79 scanf("%d%d",&n,&m); 80 memset(sm,0,sizeof(sm)); 81 for(int i=1;i<=n;i++) {scanf("%d",&a[i]);wr[a[i]]=i;} 82 upd(0); 83 tot=n; 84 for(int i=1;i<=n;i++) 85 { 86 ans+=query(i-1,a[i]); 87 change(i,a[i],1); 88 } 89 int sum=n; 90 for(int i=1;i<=m;i++) 91 { 92 int x; 93 scanf("%d",&x); 94 printf("%d\n",ans); 95 sum--; 96 change(wr[x],x,-1); 97 int now=-query(n,x); 98 now+=2*query(wr[x]-1,x); 99 ans-=sum+now-ad; 100 } 101 return 0; 102 103 }
2016-11-08 16:42:22
学了cdq分治回来更新耶!!
用CDQ分治AC啦哈哈~~
CDQ分治比树套树省空间多了,空间是O(n)的,时间还是O(nlogn^2)
然后,简单说一下?
我自己也不是很懂,刚会。。
Solve(l,r)为求出区间l~r的答案。
然后只有编号小于它的对他有影响。
然后二分 [l,mid]和[mid+1,r],,考虑完[l,mid]对[mid+1,r]的影响之后,那两个区间就可以分开搞了。(就是分治)
问题变成快速求[l,mid]对[mid+1,r]的影响。
本题中,我们可以看成是一个三元组i->(xi,yi,zi),然后ans[i]=x<xi y<yi z>zi或者x<xi y>yi z<zi 的数量。。
x这一维是时间戳,y是编号,z是值(想一想就知道他为什么表示逆序对啦!!!)
那么,只有x<xi的对xi有影响,把x这一维作为编号。
快速求出,[l,mid]对[mid+1,r]中y<yi z>zi或者y>yi z<zi 的数量
可以用y排序,树状数组维护,求和,就知道y和z的逆序对数了。。
n元组,满足x>xi,y>yi....的个数的东东 。。【这东西叫n维偏序???好像是CDQ分治的经典题哦!!
啊。。。WA了好久,竟然是没有开LL!!!!好傻啊、、
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 200010 9 #define LL long long 10 11 int a[Maxn],b[Maxn]; 12 LL f[Maxn]; 13 int n,m; 14 15 int c[Maxn]; 16 bool vis[Maxn]; 17 18 struct node 19 { 20 int x,y,id; 21 }t[Maxn];int tl; 22 23 int mymax(int x,int y) {return x>y?x:y;} 24 bool cmp(node x,node y) {return x.x>y.x;} 25 bool cmp2(node x,node y) {return x.x<y.x;} 26 27 void add(int x,int y) 28 { 29 for(int i=x;i<=n;i+=i&(-i)) 30 c[i]+=y; 31 } 32 33 int query(int x) 34 { 35 int ans=0; 36 for(int i=x;i>=1;i-=i&(-i)) 37 ans+=c[i]; 38 return ans; 39 } 40 41 void solve2(int l,int r) 42 { 43 tl=0; 44 for(int i=l;i<=r;i++) 45 { 46 t[++tl].x=a[i]; 47 t[tl].y=b[i]; 48 t[tl].id=i; 49 } 50 sort(t+1,t+1+tl,cmp); 51 for(int i=1;i<=tl;i++) 52 { 53 if(vis[t[i].id]) f[t[i].id]+=query(t[i].y); 54 else add(t[i].y,1); 55 } 56 for(int i=1;i<=tl;i++) if(!vis[t[i].id]) add(t[i].y,-1); 57 sort(t+1,t+1+tl,cmp2); 58 for(int i=1;i<=tl;i++) 59 { 60 if(vis[t[i].id]) f[t[i].id]+=query(n-t[i].y+1); 61 else add(n-t[i].y+1,1); 62 } 63 for(int i=1;i<=tl;i++) if(!vis[t[i].id]) add(n-t[i].y+1,-1); 64 } 65 66 void solve(int l,int r) 67 { 68 if(l==r) return; 69 int mid=(l+r)>>1; 70 for(int i=l;i<=mid;i++) vis[i]=0; 71 for(int i=mid+1;i<=r;i++) vis[i]=1; 72 solve2(l,r); 73 solve(l,mid);solve(mid+1,r); 74 } 75 76 int wr[Maxn],d[Maxn]; 77 void init() 78 { 79 scanf("%d%d",&n,&m); 80 for(int i=1;i<=n;i++) 81 { 82 int x; 83 scanf("%d",&x); 84 wr[x]=i; 85 } 86 for(int i=n;i>n-m;i--) 87 { 88 int x; 89 scanf("%d",&x); 90 a[i]=x; 91 b[i]=wr[x]; 92 wr[x]=0; 93 } 94 int now=n-m+1; 95 for(int i=1;i<=n;i++) if(wr[i]!=0) a[--now]=i,b[now]=wr[i]; 96 memset(c,0,sizeof(c)); 97 memset(f,0,sizeof(f)); 98 } 99 100 int main() 101 { 102 init(); 103 solve(1,n); 104 LL ans=0; 105 for(int i=1;i<=n;i++) ans+=f[i]; 106 for(int i=n;i>n-m;i--) 107 { 108 printf("%lld\n",ans); 109 ans-=f[i]; 110 } 111 return 0; 112 }
代码还不是很长啦,主要是CDQ分治空间真的要小很多。。
所以才知道,为什么大神说树套树粉转路人了ORZ。。。
2016-11-08 20:14:57