BZOJ2333

2333,好6的题号…
嘛,题目大意是维护一些点的连通性及每个联通块中的最大权值,支持的操作有加边,增加某点/联通块/所有点权值,查询某点权值,某联通块/所有点权值.
首先,题目不强制在线,我们可以先将所有的加边操作搞成 k (k为最后的联通块个数)条链,顺次打上序号后变成一个序列,可以发现,每个时刻的联通块都是一个连续的序列,于是我们可以用线段树维护最大值
代码(话说为毛加了按轶合并的并查集反而慢那么多…):

#include <bits/stdc++.h>
using namespace std;

#define For(i,n) for(int i=1;i<=n;i++)
const int MAXN=int(3*1e5+5),INF=0x3f3f3f3f;
int n,q;
int pos[MAXN],ipos[MAXN],a[MAXN];
int read(){
    int r=0;char c;bool sign=0;
    while(c=getchar(),c=='-'?sign=1:0,!isdigit(c));
    while(r=r*10+c-'0',isdigit(c=getchar()));
    return sign?-r:r;
}

struct OP {int op,x,y;} op[MAXN];
int s,t,v;
struct SGT{
    SGT *ls,*rs;
    int l,r,mid;
    int _max,add;
    SGT(){};
    SGT(int l,int r):l(l),r(r){ls=rs=0;add=0;}
    void Build();
    void Add(int c) {_max+=c;add+=c;}
    void Push_Up() {_max=max(ls->_max,rs->_max);}
    void Push_Down() {add?(ls->Add(add),rs->Add(add),add=0):0;}
    void Updata1(){
        if(l==r) {_max+=v;return;}
        Push_Down();
        s<=mid?ls->Updata1():rs->Updata1();
        Push_Up();
    }void Updata(){
        if(s<=l && t>=r) {Add(v);return;}
        Push_Down();
        if(s<=mid) ls->Updata();
        if(t> mid) rs->Updata();
        Push_Up();
    }int Query(){
        if(s<=l && t>=r) return _max;
        Push_Down();
        int _=-INF;
        s<=mid?_=max(_,ls->Query()):0;
        t> mid?_=max(_,rs->Query()):0;
        return _;
    }
}*root,Node[MAXN<<1];int mp=0;
SGT* New(int l,int r){
    Node[mp]=SGT(l,r);
    return &Node[mp++];
}void SGT::Build(){
    if(l==r) {_max=a[ipos[l]];return;}
    mid=(l+r)>>1;
    (ls=New(l,mid))->Build();
    (rs=New(mid+1,r))->Build();
    Push_Up();
}

int f[MAXN],rank[MAXN],nxt[MAXN],tai[MAXN];//f,rank:USF;nxt_x:x所连的元素,tai_x:x(联通块的代表元素)中最后的一个元素;
void Init_(){
    memset(f,0,sizeof(f));
    memset(rank,0,sizeof(rank));
    memset(nxt,0,sizeof(nxt));
    For(i,n) tai[i]=i;
}int P(int x) {return !f[x]?x:f[x]=P(f[x]);}
void Union(int x,int y){
    int fx=P(x),fy=P(y);
    if(fx==fy) return;
    if(rank[fx]>rank[fy]) swap(fx,fy);
    f[fx]=fy;
    nxt[tai[fy]]=fx; tai[fy]=tai[fx];//tai_fx之后无意义
    rank[fx]==rank[fy]?rank[fy]++:0;
}

void Init(){
    n=read();
    For(i,n) a[i]=read();
    q=read();
    char s[5];
    Init_();
    For(i,q){
        scanf("%s",s);
        switch (s[0]){
            case 'A':op[i].op=s[1]-'0';op[i].x=read();s[1]!='3'?op[i].y=read():0; break;
            case 'F':op[i].op=s[1]-'0'+3;s[1]!='3'?op[i].x=read():0; break;
            default:op[i].op=0;op[i].x=read();op[i].y=read();Union(op[i].x,op[i].y);
        }
    }
    int dfn=1;
    For(i,n) if(!f[i])
        for(int j=i;j;j=nxt[j])
            ipos[dfn]=j,pos[j]=dfn++;
    (root=New(1,n))->Build();
    Init_();
}

int main(){
#ifdef bhiaibogf
    freopen("in.in","r",stdin);
#endif
    Init();
    int x;
    For(i,q) switch (op[i].op){
        case 0:Union(op[i].x,op[i].y); break;
        case 1:s=pos[op[i].x],v=op[i].y;root->Updata1(); break;
        case 2:x=P(op[i].x);s=pos[x],t=pos[tai[x]],v=op[i].y;root->Updata(); break;
        case 3:s=1,t=n,v=op[i].x;root->Updata(); break;
        case 4:s=t=pos[op[i].x];printf("%d\n",root->Query()); break;
        case 5:x=P(op[i].x);s=pos[x],t=pos[tai[x]];printf("%d\n",root->Query()); break;
        case 6:s=1,t=n;printf("%d\n",root->Query()); break;
    }
    return 0;
}

当然,在线算法也不难写…
我们只需要一个可并堆,斜堆好啊…对于全局最大值,我们可以再开个set
斜堆就是在合并两个堆A,B(不妨设A的堆顶元素大)时,将A的右子树与B合并作为新的右子树,并将左右子树交换,期望是 O(logn) 的.注意标记要从堆顶向下传…
代码:

#include <bits/stdc++.h>
using namespace std;

#define Insert(x) _max.insert(x)
#define Erase(x) _max.erase(_max.find(x))
#define ls s[0]
#define rs s[1]
#define hu heap[u]
#define hv heap[v]
const int MAXN=int(3*1e5+5),INF=0x3f3f3f3f;
int n,_add;
multiset<int> _max;
int read(){
    int r=0;char c;bool sign=0;
    while(c=getchar(),c=='-'?sign=1:0,!isdigit(c));
    while(r=r*10+c-'0',isdigit(c=getchar()));
    return sign?-r:r;
}

struct Node{
    Node *s[2],*f;
    int x,add;
    Node(){};
    Node(int x);
    Node* Link(int d,Node *p);
    void Add(int c);
    void Push_Down();
    Node* P();
    int Top();
    Node* Remove();
}*null=new Node(-INF),heap[MAXN],*p,*q;
Node* Merge(Node *p,Node *q){
    if(p==null) return q;
    if(q==null) return p;
    if(p->x<q->x) swap(p,q);
    p->Push_Down();
    p->Link(1,Merge(p->rs,q));
    swap(p->ls,p->rs);
    return p;
}Node::Node(int x):x(x){
    Insert(x);
    add=0;
    ls=rs=f=null;
}Node* Node::Link(int d,Node *p){
    s[d]=p;
    return p==null?this:p->f=this;
}void Node::Add(int c){
    if(this==null) return;
    add+=c;x+=c;
}void Node::Push_Down(){
    if(this==null) return;
    f->Push_Down();
    add?(ls->Add(add),rs->Add(add),add=0):0;
}Node* Node::P(){
    Node *p;
    for(p=this;p->f!=null;p=p->f);
    return p;
}int Node::Top(){
    return P()->x;
}Node* Node::Remove(){
    Push_Down();
    p=f,q=Merge(ls,rs);
    f=ls=rs=null;
    if(p==null) return q->f=null,q;
    p->Link(p->rs==this,q);
    return p->P();
}

int u,v;
void A1(){
    u=read();v=read();
    Erase(hu.Top());
    hu.Push_Down();
    hu.x+=v;
    Insert(Merge(&hu,hu.Remove())->Top());
}void A2(){
    u=read();v=read();
    p=hu.P();
    Erase(p->x);
    p->Add(v);
    Insert(p->x);
}void U(){
    u=read();v=read();
    p=hu.P(),q=hv.P();
    if(p!=q) Merge(p,q)==p?Erase(q->x):Erase(p->x);
}

int main(){
#ifdef bhiaibogf
    freopen("in.in","r",stdin);
#endif
    n=read();
    for(int i=1;i<=n;i++) heap[i]=Node(read());
    int q=read();char op[5];
    while(q--){
        scanf("%s",op);
        switch (op[0]){
            case 'A':switch (op[1]){
                case '1':A1();break; case '2':A2();break; case '3':_add+=read();break;
            }break;
            case 'F':switch (op[1]){
                case '1':u=read();hu.Push_Down();printf("%d\n",hu.x+_add);break;
                case '2':u=read();printf("%d\n",hu.Top()+_add);break;
                case '3':printf("%d\n",*--_max.find(INF)+_add);break;
            }break;
            default:U();
        }
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值