【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 }
View Code

 

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 }
View Code

 

代码还不是很长啦,主要是CDQ分治空间真的要小很多。。

所以才知道,为什么大神说树套树粉转路人了ORZ。。。

 

2016-11-08 20:14:57

posted @ 2016-11-08 16:37  konjak魔芋  阅读(369)  评论(0编辑  收藏  举报