[BZOJ 3249] Game

Description

給一個R×C 的矩陣,初始時矩陣中所有元素均爲0,每次操作修改某個格子中的數,每次查詢問給定一個矩形中所有數的最大公約數。定義0和0的最大公約數爲0。
數據範圍:R,C109 ,修改次數22000 ,查詢次數250000 ,矩陣中的數1018
總時限:250s,空間限制230MB

題目傳送門

Tag

線段樹套平衡樹



Solution

網上沒有找到可讀性高的題解,再加上我作死想寫結構體嵌套。。於是很痛苦地寫了半天。。
如果能離線的話那這就是個絕世水題了。隨手離散化一波然後倆線段樹套一起就能過。
然而IOI原題是函數填充,聽說是要強制在線的。
於是離散化就萎掉了。
不能離散化的話,強行動態開點線段樹親測會MLE。
於是我們把內層的線段樹換成一個Splay。
具體來說,先按橫座標建外層的區間線段樹,線段樹的每個結點上開一棵Splay。
不妨設線段樹結點的區間爲[x1,x2] ,Splay上結點所代表的座標爲y ,以該節點爲根的子樹所代表的區間爲[y1,y2] ,於是我們在這個Splay結點上存這麼幾樣東西:

  • 子矩形[x1,x2]×[y1,y2] 中所有數的最大公約數
  • 子矩形[x1,x2]×{y} 中所有數的最大公約數
  • 座標y 的值

每次修改的時候我們都要更新外層線段樹上一條鏈上的所有Splay。
分析了這麼多,我們就可以愉快地亂搞了。
這裏有一個常數級的優化:在將Splay的某結點旋轉到根的過程中,我們要將這個點maintain 好幾次,而maintain 中自帶求gcd,於是很容易被卡常。但我們不難發現中間過程的maintain 毫無意義,於是我們就可以省掉一堆無用的maintain ,親測能使運行總時間降低10秒左右。



AC-Code

時間複雜度:O(Qlog3R)
空間複雜度:O(QlogR)

#include<bits/stdc++.h>
using namespace std;
namespace FastIO
{
    static char c;
    static int buf[22],top;
    template<typename T>inline void read(T& x)
    {
        x=0,c=getchar();
        while(!isdigit(c))c=getchar();
        while(isdigit(c))x=x*10+c-'0',c=getchar();
    }
    template<typename T>inline void write(T x)
    {
        top=0;
        do buf[top++]=x%10,x/=10;while(x);
        while(top)putchar(48+buf[--top]);
        putchar('\n');
    }
}
using FastIO::read;
using FastIO::write;
typedef long long LL;

LL gcd(LL,LL);
struct Segment
{
    struct Splay
    {
        Splay *fa,*son[2];
        LL val,GCD;
        int pos;
        Splay(){} 
        Splay(int p,LL v,Splay* f)
        {
            pos=p,val=v,fa=f;
            GCD=v,son[0]=son[1]=NULL;
        }
        inline bool cmp(int _){return _>pos;}
        inline int dir(){return this==fa->son[1];}
        inline void maintain()
        {
            GCD=val;
            if(son[0])GCD=gcd(GCD,son[0]->GCD);
            if(son[1])GCD=gcd(GCD,son[1]->GCD);
        }
    }*root;
    int l,r;
    Segment *son[2];
    Segment(){}
    Segment(int l,int r):l(l),r(r){root=NULL;son[0]=son[1]=NULL;}
    inline void rotate(Splay* o)
    {
        Splay* p=o->fa;
        int d=o->dir();
        p->son[d]=o->son[d^1];
        if(o->son[d^1])o->son[d^1]->fa=p;
        o->fa=p->fa;
        if(p->fa)p->fa->son[p->dir()]=o;
        p->fa=o,o->son[d^1]=p;
        p->maintain();
    }
    inline void splay(Splay* o,const Splay* f)
    {
        Splay* p;
        while(o->fa!=f)
        {
            p=o->fa;
            if(p->fa==f)rotate(o);
            else if(o->dir()==p->dir())rotate(p),rotate(o);
            else rotate(o),rotate(o);
        }
        if(!f)root=o;
        o->maintain();
    }
    inline Splay* Splay_insert(Splay* &o,Splay* p,int pos,LL _)
    {
        if(!o)return o=new Splay(pos,_,p);
        else if(o->pos==pos)
        {
            o->val=_,o->maintain();
            return o;
        }
        else return Splay_insert(o->son[o->cmp(pos)],o,pos,_);
    }
    inline Splay* Find(int pos)
    {
        Splay* tmp=root;
        while(tmp)
        {
            if(tmp->pos==pos)return tmp;
            tmp=tmp->son[tmp->cmp(pos)];
        }
        return NULL;
    }
    inline void insert(int pos)
    {
        Splay *tmp;
        LL __=0;
        for(int i=0;i<2;i++)if(son[i])
        {
            tmp=son[i]->Find(pos);
            if(tmp)__=gcd(__,tmp->val);
        }
        splay(Splay_insert(root,NULL,pos,__),NULL);
    }
    template<typename T>inline Splay* Kth(int _,T cmp)
    {
        Splay* ans=NULL;
        int d;
        for(Splay* o=root;o;o=o->son[d])
        {
            if(cmp(o->pos,_))ans=o;
            if(o->pos!=_)d=o->cmp(_);
            else d=cmp(2,1);
        }
        return ans;
    }
    inline LL query(int l,int r)
    {
        Splay *tmp1=Kth(r,greater<int>()),*tmp2=Kth(l,less<int>());
        if(tmp1)
        {
            splay(tmp1,NULL);
            if(tmp2)
            {
                splay(tmp2,tmp1);
                return root->son[0]->son[1]?root->son[0]->son[1]->GCD:0;
            }
            return root->son[0]?root->son[0]->GCD:0;
        }
        else if(tmp2)
        {
            splay(tmp2,NULL);
            return root->son[1]?root->son[1]->GCD:0;
        }
        else return root?root->GCD:0;
    }
}*root;
int N,M,Q,opt,p,q,u,v;
LL k;

inline LL gcd(LL a,LL b)
{
    while(b)a%=b,swap(a,b);
    return a;
}
void update(int x,int y,LL _,Segment* &o,int L=1,int R=M)
{
    if(!o)o=new Segment(L,R);
    if(L==R)return o->splay(o->Splay_insert(o->root,NULL,x,_),NULL);
    int mid=(L+R)>>1;
    if(mid>=y)update(x,y,_,o->son[0],L,mid);
    else update(x,y,_,o->son[1],mid+1,R);
    o->insert(x);
}
LL query(int x1,int x2,int y1,int y2,Segment* o,int L=1,int R=M)
{
    if(!o)return 0;
    if(L==y1&&y2==R)return o->query(x1,x2);
    int mid=(L+R)>>1;
    if(mid>=y2)return query(x1,x2,y1,y2,o->son[0],L,mid);
    else if(mid<y1)return query(x1,x2,y1,y2,o->son[1],mid+1,R);
    else return gcd(query(x1,x2,y1,mid,o->son[0],L,mid),query(x1,x2,mid+1,y2,o->son[1],mid+1,R));
}

int main()
{
    read(N),read(M),read(Q);
    for(int i=1;i<=Q;i++)
    {
        read(opt),read(p),read(q),++p,++q;
        if(opt==1)read(k),update(p,q,k,root);
        else
        {
            read(u),read(v),++u,++v;
            if(u<p)swap(p,u);
            if(v<q)swap(q,v);
            write(query(p,u,q,v,root));
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章