【集訓試題】SiriusRen的卡牌 平衡樹

題意概述:

給出N張卡牌,每張有三個屬性a,b,c,同時給出所有屬性可能的最大值A,B,C。對於一張卡牌,當這張卡牌至少有兩個屬性大於另外一張卡牌的對應兩個屬性的時候,認爲這張卡牌更加優秀。現在問有多少種不同的卡牌優於給出的所有卡牌。

N < =500000,0 < A,B,C < =500000

分析:

來自己製造一張卡牌。先把所有卡牌按照a排個序,觀察當自制卡牌的a=ai的時候會發生什麼。可以觀察到對於任意aj(1 <= j < i),都有a>aj,所以對於這些卡只需要在bj,cj中有任意至少一個屬性小於b,c的時候自制卡牌就是可行的;而對於任意ak(i<=k<=N),都有a<=ak,所以對於這些卡牌,自制卡牌的b,c必須要嚴格大於其中任意一張的b,c才行。

所以說這個時候就轉化爲了一個二維的問題,對於任意aj來說,它們的bj,cj對自制卡牌的限制取交集就是它們對自制卡牌的限制可行範圍,反過來可以發現對應的不可行範圍正好由一些以原點作爲左下角,(bj,cj)作爲右上角隨着bj增長,cj嚴格遞減的矩形圍成。而所有的ak對自制卡牌的限制則是一個右上角爲(B,C)的矩形。

於是可以維護兩條直線x=lb和y=lc表示此時ak對自制卡牌的限制可行區域,先把所有的點全部甩到set裏面去,自制卡牌的a初始值爲A,顯然此時lb=lc=0,算出這個時候的可行區域面積s,則自制卡牌的a屬於(a[N],A]的時候就有s*(A-a[N])種不同的卡牌優於給出的所有卡牌,其他情況類推。每枚舉一張卡牌的時候就先考慮其b,c對lb,lc的影響,更新面積。可以發現當set中維護的不可行域的點全部被刪除完之後任意情況下可行域一定是一個右上角爲(B,C)的規則矩形。這個時候就可以O(1)更新了。

思路就到這裏,注意代碼細節。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
#define inf 1e6+5
using namespace std;
const int maxn=500005;
typedef long long LL;

int N,ma,mb,mc;
struct data{ int a,b,c; }d[maxn];
struct XY{
    int x,y;
    friend bool operator < (XY a,XY b) {
        return a.x<b.x||a.x==b.x&&a.y<b.y;
    }
};
set<XY>Set;
set<XY>::iterator it,_it,l,r;

void data_in()
{
    scanf("%d%d%d%d",&N,&ma,&mb,&mc);
    for(int i=1;i<=N;i++)
        scanf("%d%d%d",&d[i].a,&d[i].b,&d[i].c);
}
bool cmp(data x,data y) { return x.a<y.a; }
void initial()
{
    sort(d+1,d+N+1,cmp);
    XY p;
    Set.insert((XY){0,inf});
    Set.insert((XY){inf,0});
    for(int i=1;i<=N;i++) {
        p=(XY){d[i].b,d[i].c}; it=Set.lower_bound(p);
        if(it->y>=p.y) continue;
        it--;
        while(it->y<=p.y) { _it=it,it--; Set.erase(_it); }
        Set.insert(p);
    }
}
void work()
{
    initial();
    int lb=0,lc=0,last=0;
    LL ans=0,s=0;
    l=Set.begin(),r=Set.end(),l++,r--;
    for(it=l;it!=r;it++) {
        s+=(LL)(it->x-last)*(mc-it->y);
        last=it->x;
    }
    s+=(LL)(mb-last)*mc,ans+=s*(ma-d[N].a),r--;
    XY p;
    for(int i=N;i>=1;i--)
    {
        p=(XY){d[i].b,d[i].c};
        while(Set.size()>2&&l->x<=p.x) {
            s-=(LL)(l->x-lb)*(mc-l->y);
            lb=l->x,it=l,l++; Set.erase(it);
        }
        if(Set.size()>2&&lb<p.x&&p.x<l->x) s-=(LL)(p.x-lb)*(mc-l->y),lb=p.x;
        while(Set.size()>2&&r->y<=p.y) {
            s-=(LL)(mb-r->x)*(r->y-lc);
            lc=r->y,it=r,r--; Set.erase(it);
        }
        if(Set.size()>2&&lc<p.y&&p.y<r->y) s-=(LL)(mb-r->x)*(p.y-lc),lc=p.y;
        if(Set.size()==2) {
            if(p.x>lb) s-=(LL)(p.x-lb)*(mc-lc),lb=p.x;
            if(p.y>lc) s-=(LL)(mb-lb)*(p.y-lc),lc=p.y;
        }
        ans+=s*(d[i].a-d[i-1].a);
    }
    cout<<ans<<'\n';
}
int main()
{
    data_in();
    work();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章