[聯合集訓6-9]Congroo 二進制分組+凸包上二分

查詢叉積最大值就是找該向量切凸包上的切點。因爲只有在末尾加入刪除的操作,我們利用二進制分組,維護log 個凸包,詢問的時候在每個凸包上二分找切點即可。
要注意要分別對上下凸殼二分,因爲凸包是一個環但二分其實是對一個序列二分,所以直接二分會掛。
程序裏還有幾個複雜度不對的地方:
1. 二進制分組應該有四個相同的才把前兩個合併,而不是有兩個相同就合併,否則每次加入/刪除第2k 個向量複雜度都是O(2k)=O(n) 的,不斷操作就掛了。
2. 合併凸包的時候可以用歸併排序把複雜度優化到O(n)
3. 加上以上優化可以做到O(nlog2n) ,但因爲沒有要求強制在線,我們可以對於在某個凸包上二分某個切點的問題最後一起離線用旋轉卡殼處理(刪掉的凸包要拷貝出來),可以優化到O(nlogn)
但以上做法時空常數巨大還很不好寫。。。出題人又沒卡,所以就懶得寫了。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
#define N 300010
using namespace std;
const int mod=998244353;
const int R=(1<<19)-1;
int type,m,cnt;
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct pt
{
    int x,y;
    ll operator *(pt b){return (ll)x*b.y-(ll)y*b.x;} 
    bool operator <(pt b)
    {
        if(x==b.x) return y<b.y;
        return x<b.x;
    }
    pt operator +(pt b)
    {
        return (pt){x+b.x,y+b.y};   
    }
    pt operator -(pt b)
    {
        return (pt){x-b.x,y-b.y};
    }
}st[N];
struct cvx
{
    vector<pt> t,s;
    int D;
    void build()
    {
        s.clear();
        s=t;        
        sort(s.begin(),s.end());
        int top=0,tmp;
        for(int i=0;i<s.size();i++)
        {   
            while(top>1&&(s[i]-st[top])*(st[top]-st[top-1])<=0) top--;
            st[++top]=s[i];
        }   
        D=top;
        for(int i=s.size()-1;i>=0;i--)
        {
            while(top>D&&(s[i]-st[top])*(st[top]-st[top-1])<=0) top--;
            st[++top]=s[i];
        }
        s.resize(top);
        for(int i=1;i<=top;i++)
            s[i-1]=st[i];
    }
    ll qry(pt p)
    {
        ll re=-1e18;
        int L=0,R=D-1;
        while(L<R)
        {
            int mid=(L+R)>>1;
            if(p*s[mid+1]-p*s[mid]<=0) R=mid;
            else L=mid+1;
        }
        re=max(re,p*s[L]);
        L=D;R=s.size()-2;
        while(L<R)
        {
            int mid=(L+R)>>1;
            if(p*s[mid+1]-p*s[mid]<=0) R=mid;
            else L=mid+1;
        }
        re=max(re,p*s[L]);
        return re;
    }
}f[R+N];
void add(pt p)
{
    cnt++;
    for(int i=R+cnt;i;i>>=1)
        f[i].t.push_back(p);
    for(int i=R+cnt;i;i>>=1)
    {
        f[i].build();
        if(!(i&1)) break;
    }   
}
void del()
{
    for(int i=R+cnt;i;i>>=1)
        f[i].t.pop_back();
    for(int i=R+cnt;i;i>>=1)
        if(!(i&1)) break;
    cnt--;
}
ll qry(int x,int l,int r,int lx,int rx,pt p)
{
    if(l==lx&&r==rx) return f[x].qry(p);
    int mid=(l+r>>1);
    if(rx<=mid) return qry(x<<1,l,mid,lx,rx,p);
    if(lx>mid) return qry(x<<1|1,mid+1,r,lx,rx,p);
    return max(qry(x<<1,l,mid,lx,mid,p),qry(x<<1|1,mid+1,r,mid+1,rx,p)); 
}
int main()
{
    type=read();m=read();
    int ans=0;
    while(m--)
    {
        int opt=read(),l,r,x,y;
        if(opt==1)
        {
            x=read();y=read();
            add((pt){x,y});
        }
        if(opt==2) del();
        if(opt==3)
        {
            l=read();r=read();x=read();y=read();
            ll tmp=qry(1,1,R+1,l,r,(pt){x,y});
            ans^=(tmp%mod+mod)%mod;
        }
    }   
    printf("%d",ans);
    return 0;
}
發佈了272 篇原創文章 · 獲贊 27 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章