A-C傳送門html
D Karen and Cardsios
技巧性很強的一道二分優化題c++
題意很簡單 給定n個三元組,和三個維度的上限,問存在多少三元組,使得對於給定的n個三元組中的每個,必有兩個維度嚴格小於。數組
首先咱們根據一個維度(c維)對n個三元組排序,而後枚舉答案在這個維度的取值。優化
此時序列被分紅了兩個部分,前半部分 知足全部c大於等於i 後半部分知足全部c嚴格小於i(即已有一個維度小於)spa
經過累計,咱們知道此時前半部a維的最大值ma和b維的最大值mb.code
顯然可能存在的三元組答案,必然首先知足a維和b維嚴格大於ma和mb.htm
後面咱們考慮對於後半部分,即c嚴格小於i的部分,可能存在某些三元組 ai和bi很是大,以至於上邊的答案不合法。blog
這時,咱們想知道,對於ai大於ma的那些三元組,其對應的bi能有多大?排序
咱們能夠用一個mx數組提早統計這個值。
那麼,當ai大到必定程度,其對應的bi就不可能大於mb了 咱們能夠二分找到這個邊界ret
對於ma到ret這個範圍的值,咱們讓第一維取其中的某個值,對應的第二維b有多少種可能呢?
全部ai大於當前值的對應的bi的最大值。 由於若ai小於當前值,就不須要保證第二維大於bi了。。
具體看代碼吧 有點難以描述。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define maxn 500005 #define ll long long using namespace std; ll sum[maxn],mx[maxn]; ll n,p,q,r; ll ans; struct note{ int a,b,c; }a[maxn]; bool cmp(note i,note j){ return i.c>j.c; } int main(){ scanf("%I64d%I64d%I64d%I64d",&n,&p,&q,&r); fo(i,1,n) scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c); fo(i,1,n) mx[a[i].a]=max(mx[a[i].a],(ll)a[i].b+1); fd(i,p,1) mx[i]=max(mx[i+1],mx[i]);//可能存在的最大b值 fo(i,1,p) sum[i]=sum[i-1]+(q-mx[i])+1; ll ma=1,mb=1; sort(a+1,a+n+1,cmp); int wz=1; fd(i,r,1) { while (wz<=n && a[wz].c==i) { ma=max(ma,a[wz].a+1ll); mb=max(mb,a[wz].b+1ll); wz++; } if (ma>p || mb>q) break; int x=ma,y=p,ret=ma-1; while (x<=y) { int mid=(x+y) >> 1; if (mx[mid]>=mb) { x=mid+1; ret=mid; } else y=mid-1; } ans+=sum[ret]-sum[ma-1]+1ll*(p-ret)*(q-mb+1); } cout<<ans; return 0; }
題意很是簡單 沒必要贅述
整個過程實際上是一個滿二叉樹的層次遍歷,問咱們遍歷到的第k個元素是哪個。對數複雜度
先給出遍歷的二叉樹 其規律很是明顯
咱們能夠在對數時間內快速尋找到第k個節點,可是咱們的空間不足以儲存全部的節點信息。
那麼怎麼辦呢? 咱們首先判斷這個節點在哪個層次,即它與鄰居的最短距離是多少。
而後咱們再二分判斷這個節點在整個序列中的位置,咱們能夠快速判斷一個區間產生可以產生多少個距離爲len的子節點。與k比較便可。
因而總的複雜度是二分套二分 即O(lognlogn)
這道題讓咱們深刻理解了滿二叉樹的層次遍歷
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll n,k; int main(){ scanf("%lld%lld",&n,&k); if(k==1)return printf("1"),0; if(k==2)return printf("%lld",n),0; n-=2,k-=3; map<ll,ll>f,g,nxt; g[n]=1; while(g.size()){ nxt.clear(); for(auto s:g)if(s.first) f[-((s.first-1)>>1)]+=s.second, nxt[(s.first)>>1]+=s.second, nxt[(s.first-1)>>1]+=s.second; g=nxt; } for(auto s:f){ //printf("[%d]",s.first); if(s.second>k){//肯定在哪一層 ll l=1,r=n,len=-s.first; // printf("[%d]",len); while(true){ if(((r-l)>>1)==len&&k==0) return printf("%lld",((r-l)>>1)+1+l),0; map<ll,ll>A,B,C; ll mid=l+r>>1; A[mid-l]=1; B[(r-l)>>1]=1; while(A.size()){ C.clear(); for(auto qs:A)if(qs.first) B[(qs.first-1)>>1]+=qs.second,// C[(qs.first)>>1]+=qs.second, C[(qs.first-1)>>1]+=qs.second; A=C; } // printf("[%d]",B[len]); if(B[len]>k)r=mid-1; else l=mid+1,k-=B[len]; } } else k-=s.second; } }