CodeForces

( ext{Description})

传送门

( ext{Pre Cheese})

一棵树的括号序列表示了这棵树。左括号代表向下走,右括号是向上走。从根节点开始走。

一个和题目没什么关系的小结论:

一棵树的括号序列长度为 (2 imes (n-1))

证明:每条边必定可以这样表示 ((......))(因为有去有回嘛)。所以一条边对应两个括号,边条数为 (n-1),括号序列长度则为 (2 imes (n-1))

( ext{Solution})

选取从 (u)(v) 的括号序列(其实这个序列只要起点到 (v) 能经过 (u) 即可,但注意到了 (v) 就必须停止(设 (u)(v) 遍历之前)),匹配的括号就消掉,剩下的括号形如 )))(((。则有:

剩余括号个数为 ( ext{dis}(u,v))

证明:

考虑到 (x) 点时某对括号(假设这对括号代表的边能从上到达 (y),再回去)的匹配状态。

  1. 均已出现。(y) 不是 (x) 的祖先或子孙。

  2. 均未出现。(y) 不是 (x) 的祖先。

  3. 出现左括号。(y)(x) 的祖先。

我们回来看 (u,v) 的括号序列。

  1. 能够匹配的括号。对于 (u) 是情况二,对于 (v) 是情况一,则“既不是 (u) 的祖先,也不是 (v) 的的祖先或子孙”。根本不在路径上。
  2. 左括号。对于 (u) 是情况二,对于 (v) 是情况三,则“不是 (u) 的祖先,但是是 (v) 的的祖先”。在路径上。
  3. 右括号。对于 (u) 是情况三,对于 (v) 是情况一,则“是 (u) 的祖先,但不是 (v) 的的祖先或子孙”。在路径上(这时肯定不在 (u,v) 的公共祖先上)。

所以结论得证。

那么如何计算呢?

如果要消去匹配的括号,我们马上就会想到给括号赋值:((1))(-1)。但这样就会使 )))((( 的计算费些周折:考虑到剩余括号个数就是 (3-(-3)=6),我们的答案转化为求相邻两段区间和的差的最大值,可以用线段树维护。

接下来为了方便,设区间的两个左右区间为 (l,r)

先思考我们要求的相邻两段区间和的差的最大值(令它为 (ans))该如何合并。首先有 (ans_l,ans_r) 的贡献,其次需要考虑跨区间的情况。我们对提供贡献的数据有这样的要求:

  1. (l) 选择的区间必须包含 (mid)(r) 选择的区间必须包含 (mid+1)
  2. 因为只能有一个分界点(即相邻两段区间的划分点),不能直接用 (ans) 来贡献,我们必须钦定分界点在 (l/r)

由此就有了数组的定义:

  • (ans):答案。
  • (sum):区间和。
  • (lma):最大前缀和。
  • (rmi):最小后缀和。
  • (ld):最大含分界点且包含左端点的答案。
  • (rd):最大含分界点且包含右端点的答案。
  • (d):最大含分界点且包含整个区间的答案。

如何维护相信大家都会了。我是真的不想写了。

( ext{Code})

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}

#include <iostream>
using namespace std;

const int maxn=2e5+5;

int n,m,a[maxn],ans[maxn<<2],sum[maxn<<2],lma[maxn<<2],rmi[maxn<<2],ld[maxn<<2],rd[maxn<<2],d[maxn<<2];
char str[maxn];

void pushUp(int o) {
  int ls=(o<<1),rs=(o<<1|1);
  sum[o]=sum[ls]+sum[rs];
  lma[o]=Max(lma[ls],sum[ls]+lma[rs]);
  rmi[o]=Min(rmi[rs],sum[rs]+rmi[ls]);
  ld[o]=Max(ld[ls],Max(d[ls]+lma[rs],ld[rs]-sum[ls]));
  rd[o]=Max(rd[rs],Max(d[rs]-rmi[ls],sum[rs]+rd[ls]));
  d[o]=Max(d[ls]+sum[rs],d[rs]-sum[ls]);
  ans[o]=Max(Max(ans[ls],ans[rs]),Max(ld[rs]-rmi[ls],rd[ls]+lma[rs]));
}

void Insert(int o,int l,int r,int pos,int k) {
  if(l>pos||r<pos) return;
  if(l==r) {
    sum[o]=k;
    lma[o]=Max(k,0),rmi[o]=Min(k,0);
    ld[o]=rd[o]=ans[o]=d[o]=1;
    return;
  }
  int mid=l+r>>1;
  Insert(o<<1,l,mid,pos,k); Insert(o<<1|1,mid+1,r,pos,k);
  pushUp(o);
}

int main() {
  n=read(9)*2-2,m=read(9),scanf("%s",str+1);
  rep(i,1,n) {
    if(str[i]=='(') a[i]=1;
    else a[i]=-1;
    Insert(1,1,n,i,a[i]);
  }
  print(ans[1],'
');
  int x,y;
  while(m--) {
    x=read(9),y=read(9);
    if(a[x]==a[y]) {print(ans[1],'
'); continue;}
    swap(a[x],a[y]);
    Insert(1,1,n,x,a[x]); Insert(1,1,n,y,a[y]);
    print(ans[1],'
');
  }
  return 0;
}

参考资料

command_block.

原文地址:https://www.cnblogs.com/AWhiteWall/p/14223945.html