bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)

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

需要改变的括号序列一定长这样 :)))(((

最少改变次数= 多余的‘)’/2 【上取整】 + 多余的‘(’ /2 【上取整】

把 ‘)’ 看做1,‘(’ 看做-1

那么最少改变次数=最大前缀和/2 【上取整】+ 最小后缀和/2 【上取整】

覆盖标记的优先级高于翻转标记和取反标记

即下放覆盖标记时,同时清空翻转标记和取反标记

且先下放覆盖标记

翻转:

最大前缀和 和 最大后缀和 交换

最小前缀和 和 最小后缀和 交换

取反:

最大前缀和 和 最小前缀和 交换,同时取反

最大后缀和 和 最小后缀和 交换,同时取反

最大XX和的下界为0,最小XX和的上界为0

因为最大XX和实际是多余的‘)’数量

最小XX和的相反数实际是多余的‘(’数量

数量不能为负数

注意点:

1、增加了首尾两个虚拟节点后,数组要多开2

2、平衡树每个节点由三部分组成,左子树、自己、右子树,打取反标记的时候不要忘记给自己取反

#include<cstdio>
#include<cstdlib>
#include<iostream>

using namespace std;

#define N 100003

char ss[N];
int a[N];

int root,tot;

int st[N];

bool rev[N],inv[N];
int tag[N];
int pre[N],suf[N];
int sum[N];
int pri[N],val[N];

int siz[N],ch[N][2];

int tmp;

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

inline int &max(int &a,int &b) { return a>b ? a : b; }
inline int &min(int &a,int &b) { return a<b ? a : b; }

void update(int x)
{
    int l=ch[x][0],r=ch[x][1];
    siz[x]=siz[l]+siz[r]+1;
    sum[x]=sum[l]+sum[r]+val[x];
    pre[x]=max(pre[l],sum[l]+pre[r]+val[x]);
    pre[x]=max(pre[x],sum[l]+val[x]);
    suf[x]=max(suf[r],sum[r]+suf[l]+val[x]);
    suf[x]=max(suf[x],sum[r]+val[x]);
}

int newnode(int v)
{
    val[++tot]=sum[tot]=v;
    pre[tot]=suf[tot]=max(0,val[tot]);
    sum[tot]=val[tot];
    siz[tot]=1;
    pri[tot]=rand();
    return tot;
}

int build(int l,int r)
{
    int top=0; int last,now;
    for(int i=l;i<=r;++i)
    {
        now=newnode(a[i]);
        last=0;
        while(top && pri[now]<pri[st[top]])
        {
            update(st[top]);
            last=st[top--];
        }
        if(top) ch[st[top]][1]=now;
        ch[now][0]=last;
        st[++top]=now;
    }
    while(top) update(st[top--]);
    return st[1];
}

void down(int x)
{
    int l=ch[x][0],r=ch[x][1];
    if(tag[x])
    {
        if(l)
        {
            val[l]=tag[x];
            sum[l]=tag[x]*siz[l];
            pre[l]=suf[l]=max(0,sum[l]);
            rev[l]=inv[l]=false;
            tag[l]=tag[x];
        }
        if(r)
        {
            val[r]=tag[x];
            sum[r]=tag[x]*siz[r];
            pre[r]=suf[r]=max(0,sum[r]);
            rev[r]=inv[r]=false;
            tag[r]=tag[x];
        }
        tag[x]=0;
    }
    if(rev[x])
    {
        if(l)
        {
            swap(pre[l],suf[l]);
            swap(ch[l][0],ch[l][1]);
            rev[l]^=1;
        }
        if(r)
        {
            swap(pre[r],suf[r]);
            swap(ch[r][0],ch[r][1]);
            rev[r]^=1;
        }
        rev[x]^=1;
    }
    if(inv[x])
    {
        if(l)
        {
            tmp=pre[l];
            pre[l]=max(0,-(sum[l]-suf[l]));
            suf[l]=max(0,-(sum[l]-tmp));
            sum[l]=pre[l]+min(0,-sum[l]-pre[l]);
            val[l]=-val[l];
            inv[l]^=1;
        }
        if(r)
        {
            tmp=pre[r];
            pre[r]=max(0,-(sum[r]-suf[r]));
            suf[r]=max(0,-(sum[r]-tmp));
            sum[r]=pre[r]+min(0,-sum[r]-pre[r]);
            val[r]=-val[r];
            inv[r]^=1;
        }
        inv[x]^=1;
    }
}

void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else
    {
        down(now);
        if(k<=siz[ch[now][0]])
        {
            y=now;
            split(ch[now][0],k,x,ch[now][0]);
        }
        else
        {
            x=now;
            split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y);
        }
        update(now);
    }
}

int merge(int x,int y)
{
    if(x) down(x);
    if(y) down(y);
    if(!x || !y) return x+y;
    if(pri[x]<pri[y])
    {
        ch[x][1]=merge(ch[x][1],y);
        update(x);
        return x;
    }
    else
    {
        ch[y][0]=merge(x,ch[y][0]);
        update(y);
        return y;
    }
}

int main()
{
    int n,m;
    read(n); read(m);
    scanf("%s",ss+2);
    for(int i=2;i<=n+1;++i)
        if(ss[i]==‘)‘) a[i]=1;
        else a[i]=-1;
    root=build(1,n+2);
    int l,r; char s[10],cc[3];
    int a,b,c,d,e;
    while(m--)
    {
        scanf("%s",s);
        read(l); read(r);
        l++; r++;
        split(root,r,a,b);
        split(a,l-1,c,d);
        if(s[0]==‘R‘)
        {
            scanf("%s",cc);
            e= cc[0]==‘)‘ ? 1 : -1;
            tag[d]=e;
            val[d]=e;
            sum[d]=e*siz[d];
            pre[d]=suf[d]=max(0,sum[d]);
            rev[d]=inv[d]=false;
        }
        else if(s[0]==‘S‘)
        {
            rev[d]=true;
            swap(ch[d][0],ch[d][1]);
            swap(pre[d],suf[d]);
        }
        else if(s[0]==‘I‘)
        {
            inv[d]=true;
            tmp=pre[d];
            pre[d]=max(0,-(sum[d]-suf[d]));
            suf[d]=max(0,-(sum[d]-tmp));
            sum[d]=pre[d]+min(0,-sum[d]-pre[d]);
            val[d]=-val[d];
        }
        else printf("%d\n",(pre[d]+1)/2+(-sum[d]+pre[d]+1)/2);
        root=merge(merge(c,d),b);
    }
}

2329: [HNOI2011]括号修复

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 1229  Solved: 580
[Submit][Status][Discuss]

Description

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

时间: 2024-08-25 06:24:52

bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)的相关文章

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千题计划252:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 点分树+堆 请去看 http://www.cnblogs.com/TheRoadToTheGold/p/8463436.html 线段树维护括号序列 对树进行dfs,入栈时加一个左括号,出栈时加一个右括号,那么书上两点间的距离=括号序列两点间不匹配括号数 例: 树1--2--3,2为根 括号序列为 (2(3)(1)) 2和1的距离 为 ()( = 1, 3和1的距离为 )( =2 具体怎么维

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

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

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千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();