洛谷 P3810 【模板】三維偏序(陌上花開) (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.修改對答案的貢獻是確定的,與判定標准無關

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM