bzoj千题计划217:bzoj2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333

读入所有数据,先模拟一遍所有的合并操作

我们不关心联通块长什么样,只关心联通块内有谁

所以可以把一个联通块用一个链表存储

合并x和y时,y的链表整体接到x的链表后面

这样就成了线性结构

按照链表顺序重新给序列标号即可用线段树维护

一遍过,^_^

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 300001

int a[N];

struct Data
{
    char s[3];
    int x,y;
}data[N];

int fa[N],nxt[N],ed[N];

int id[N],dy[N];

int mx[N<<2],f[N<<2];

int ans;

void read(int &x)
{
    x=0; int f=1; char c=getchar();
    while(!isdigit(c)) { if(c==‘-‘) f=-1; c=getchar(); }
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
    x*=f;
}

void build(int k,int l,int r)
{
    if(l==r)
    {
        mx[k]=a[id[l]];
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
}

void down(int k)
{
    mx[k<<1]+=f[k];
    mx[k<<1|1]+=f[k];
    f[k<<1]+=f[k];
    f[k<<1|1]+=f[k];
    f[k]=0;
}

void add(int k,int l,int r,int opl,int opr,int w)
{
    if(l>=opl && r<=opr)
    {
        f[k]+=w;
        mx[k]+=w;
        return;
    }
    if(f[k]) down(k);
    int mid=l+r>>1;
    if(opl<=mid) add(k<<1,l,mid,opl,opr,w);
    if(opr>mid) add(k<<1|1,mid+1,r,opl,opr,w);
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
}

void query(int k,int l,int r,int opl,int opr)
{
    if(l>=opl && r<=opr)
    {
        ans=max(ans,mx[k]);
        return;
    }
    if(f[k]) down(k);
    int mid=l+r>>1;
    if(opl<=mid) query(k<<1,l,mid,opl,opr);
    if(opr>mid) query(k<<1|1,mid+1,r,opl,opr);
}

int find(int i)
{
    return fa[i]==i ? i : fa[i]=find(fa[i]);
}

int main()
{
    int n,m;
    read(n);
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1;i<=n;++i) fa[i]=i,ed[i]=i;
    char s[5]; int x,y;
    int fx,fy;
    read(m);
    for(int i=1;i<=m;++i)
    {
        scanf("%s",data[i].s);
        if(!(data[i].s[0]==‘F‘ && data[i].s[1]==‘3‘)) read(data[i].x);
        if(data[i].s[0]==‘U‘ || data[i].s[0]==‘A‘ && data[i].s[1]!=‘3‘) read(data[i].y);
        if(data[i].s[0]==‘U‘)
        {
            fx=find(data[i].x);
            fy=find(data[i].y);
            nxt[ed[fx]]=fy;
            ed[fx]=ed[fy];
            fa[fy]=fx;
        }
    }
    int tot=0;
    for(int i=1;i<=n;++i)
        if(find(i)==i)
        {
            int j=i;
            while(j!=ed[i])
            {
                id[++tot]=j;
                dy[j]=tot;
                j=nxt[j];
            }
            id[++tot]=j;
            dy[j]=tot;
        }
    build(1,1,n);
    for(int i=1;i<=n;++i) fa[i]=i,ed[i]=i;
    int all=0;
    for(int i=1;i<=m;++i)
    {
        if(data[i].s[0]==‘U‘)
        {
            fx=find(data[i].x);
            fy=find(data[i].y);
            nxt[ed[fx]]=fy;
            ed[fx]=ed[fy];
            fa[fy]=fx;
        }
        else if(data[i].s[0]==‘A‘)
        {
            if(data[i].s[1]==‘1‘)  add(1,1,n,dy[data[i].x],dy[data[i].x],data[i].y);
            else if(data[i].s[1]==‘2‘) add(1,1,n,dy[find(data[i].x)],dy[ed[find(data[i].x)]],data[i].y);
            else all+=data[i].x;
        }
        else
        {
            if(data[i].s[1]==‘1‘)
            {
                ans=-1e9;
                query(1,1,n,dy[data[i].x],dy[data[i].x]);
                printf("%d\n",ans+all);
            }
            else if(data[i].s[1]==‘2‘)
            {
                ans=-1e9;
                query(1,1,n,dy[find(data[i].x)],dy[ed[find(data[i].x)]]);
                printf("%d\n",ans+all);
            }
            else printf("%d\n",mx[1]+all);
        }
    }
}

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8387625.html

时间: 2024-10-23 09:04:42

bzoj千题计划217:bzoj2333: [SCOI2011]棘手的操作的相关文章

bzoj千题计划242:bzoj4034: [HAOI2015]树上操作

http://www.lydsy.com/JudgeOnline/problem.php?id=4034 dfs序,树链剖分 #include<cstdio> #include<iostream> using namespace std; #define N 100001 typedef long long LL; int n,a[N]; int front[N],nxt[N<<1],to[N<<1],tot; int fa[N],siz[N],dep[N]

bzoj2333[SCOI2011]棘手的操作

bzoj2333[SCOI2011]棘手的操作 题意: 有N个节点,M个操作:连接两个节点.单个节点的权值增加v.节点所在的连通块的所有节点的权值增加v.所有节点的权值增加v.询问节点当前的权值.询问节点所在的连通块中权值最大的节点的权值.询问所有节点中权值最大的节点的权值.N,M≤300000 题解: 可并堆,虽然听说配对堆非常快,但教程太少了不会写,所以去学了斜堆,比较好写.斜堆实际上是一棵二叉树,核心是合并操作,这是一个递归过程,有点像treap的删除操作.斜堆保证复杂度的方法是每次递归合

bzoj千题计划218:bzoj2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 上次那个是线段树,再发一个左偏树 维护两种左偏树 第一种是对每个联通块维护一个左偏树 第二种是对所有第一种左偏树的根节点维护一个左偏树 #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 300001 /*void read(int &x

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n