洛谷 P3810 【模板】三维偏序(陌上花开) (cdq分治模板)

cdq分治的基本思想:

在solve(L,R)中,需要先分治solve两个子区间,再计算左边区间修改对右边区间询问的贡献。

计算额外的贡献时,两子区间各自内部的顺序变得不再重要(不管怎么样左边区间的都发生在右边之前),于是就少了一维


https://www.lydsy.com/JudgeOnline/problem.php?id=3262

https://www.luogu.org/problemnew/show/P3810

此题每个操作既是修改又是查询

对于此题,先按第一维排序,在solve(L,R)中先solve两个子区间,然后把L到R的操作按第二维排序(由于cdq分治类似归并、自底向上的特性,此时两个子区间内部第二维都是有序的(递归过程中操作得到的),可以直接二路归并),然后就是一个对第二、三维求逆序对的过程(只不过只有归并前在第一个区间内的修改生效,只有归并前在第二个区间内的查询要更新答案)

可以记一下每个元素在按第一维排序后的编号(以下代码中q[i].num),来判断它归并前是哪个区间里的

注意:此题第一维相同的之间实际并不存在“第一维方面的”顺序关系,理应同时处理然后同时计算贡献,为了方便没有这么干。但排序后它们间总是要存在一个特定顺序的,导致这个顺序里后面对前面也会产生贡献,所以要加一些奇怪的特判

具体的话:首先一开始排序的时候三个关键字都要依次考虑(而不是只考虑第一维),这样可以保证排序后大部分情况下后面的不会对前面产生贡献

上面还漏考虑了完全相等的三元组,如果它们存在则后面也会对前面产生贡献。因此只要在开始solve前补上这些后面对前面产生的贡献即可

归并可以简化为

merge(q+lp,q+mid+1,q+mid+1,q+rp+1,tmp+lp);
copy(tmp+lp,tmp+rp+1,q+lp);

inplace_merge(q+lp,q+mid+1,q+rp+1);
 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 struct Q
 5 {
 6     int a,b,c,ans,num;
 7 }q[100100],tmp[100100];
 8 int n,k;
 9 bool c1(const Q &a,const Q &b)    {return a.a<b.a||(a.a==b.a&&a.b<b.b)||(a.a==b.a&&a.b==b.b&&a.c<b.c);}
10 bool operator<(const Q &a,const Q &b)    {return a.b<b.b||(a.b==b.b&&a.num<b.num);}
11 bool operator==(const Q &a,const Q &b)    {return a.a==b.a&&a.b==b.b&&a.c==b.c;}
12 int dat[200100];
13 const int N=200000;
14 #define lowbit(x) ((x)&(-x))
15 void addx(int pos,int d)
16 {
17     for(;pos<=N;pos+=lowbit(pos))    dat[pos]+=d;
18 }
19 int sum(int pos)
20 {
21     int ans=0;
22     for(;pos>0;pos-=lowbit(pos))    ans+=dat[pos];
23     return ans;
24 }
25 int num[100100];
26 void solve(int lp,int rp)
27 {
28     if(lp==rp)    return;
29     int mid=lp+(rp-lp)/2;
30     solve(lp,mid);solve(mid+1,rp);
31     int k=lp-1,i,j;
32     for(i=lp,j=mid+1;i<=mid&&j<=rp;)
33     {
34         ++k;
35         if(q[i]<q[j])    tmp[k]=q[i++];
36         else    tmp[k]=q[j++];
37     }
38     while(i<=mid)    tmp[++k]=q[i++];
39     while(j<=rp)    tmp[++k]=q[j++];
40     for(i=lp;i<=rp;i++)    q[i]=tmp[i];
41     for(i=lp;i<=rp;i++)
42     {
43         if(q[i].num<=mid)    addx(q[i].c,1);
44         else    q[i].ans+=sum(q[i].c);
45     }
46     for(i=lp;i<=rp;i++)
47         if(q[i].num<=mid)
48             addx(q[i].c,-1);
49 }
50 int main()
51 {
52     int i,j,t,tt=0;
53     scanf("%d%d",&n,&t);
54     for(i=1;i<=n;i++)    scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c);
55     sort(q+1,q+n+1,c1);
56     for(i=1;i<=n;i++)
57     {
58         tt++;
59         if(i==n||!(q[i]==q[i+1]))
60         {
61             for(j=i-tt+1,t=tt-1;j<=i;j++)    q[j].ans+=t,--t;
62             tt=0;
63         }
64     }
65     for(i=1;i<=n;i++)    q[i].num=i;
66     solve(1,n);
67     for(i=1;i<=n;i++)    num[q[i].ans]++;
68     for(i=0;i<n;i++)    printf("%d\n",num[i]);
69     return 0;
70 }

来看看cdq分治的限制

1.题目允许离线操作
2.修改操作对询问的贡献独立,且修改之间互不影响
3.修改对答案的贡献是确定的,与判定标准无关(我得冷静一下)

 

posted @ 2018-05-12 13:32  hehe_54321  阅读(1127)  评论(0编辑  收藏  举报
AmazingCounters.com