[科技] 假装是ETT的ETT

[科技] 假装是ETT的ETT

[科技] 假装是ETT的ETT

Codechef 的 April Challenge 2019 Division 1 的 Sonya and Queries 这题的\(45\)分部分分,似乎是一个出栈入栈序\(ETT\),看着似乎还挺好的,就写了写。那么这里就讲一讲这个假装是\(ETT\)\(ETT\)

\(ETT\),全称\(Euler-Tour-Tree\)。实际上真实的\(ETT\)是用树上的欧拉回路来写的,但是也有这种省略欧拉回路为入栈出栈序列的\(ETT\),个人感觉可能后者更容易实现,但是与前者的不同就是似乎后者不能换根,但是可以进行链上操作……如果要学欧拉回路的\(ETT\)的话可以看这个文章

首先出栈入栈序列,或者叫做括号序这个东西,用处还是挺多的,比较常见的就是用在\(RMQ\ LCA\)的问题上,可以做到\(O(nlogn)\)预处理,\(O(1)\)查询\(LCA\)的优秀复杂度。其主要的性质就是对于每一个点的出栈与入栈时间\(in[x]\)\(out[x]\),都有\([in[x], out[x]]\)中所有的节点都是\(x\)的子树中的节点。这个还是比较显然的,这也是\(ETT\)能够在某些地方代替\(LCT\)维护动态树的基础。

于是我们尝试用括号序\(ETT\)实现一些\(LCT\)的操作。

首先是最基础的\(Link / Cut\)

考虑到\(Link / Cut\)就是把\(x\)的子树和\(x\)的父亲断开或者接到另一个节点\(y\)上,而这个操作在括号序上的表现即是序列上一个连续段的平移。这个操作我们再熟悉不过了,利用平衡树就可以优秀地解决了。

然后是一些基础的子树操作,例如子树加,子树赋值,子树求和,子树求\(Max\)等等:

还是考虑括号序的性质,\(x\)的子树都在\([in[x], out[x]]\)这个区间中,在平衡树中将这个区间分裂出来之后找到根节点,就可以得到区间的信息了,或者打一些子树的标记了。不过需要注意的是,在子树求和的时候,由于区间中的数实际上有\(2 * sz\)个,所以答案应该要除以\(2\)

还有一写单点的操作也是和子树操作类似的,就不赘述了。但是有一个细节也是和子树求和差不多的,就是单点操作的时候要操作两个点,这点不要忘记。

然后是一些链上的操作:

(注:这里的链上操作,博主似乎都只会那种直接到根节点的操作,并且这种方法似乎不能求链上\(\min, \max\),如果有什么更为优秀的实现方式,欢迎评论)
考虑括号序的实质是入栈出栈序列,假设我们需要操作是\(x\)到根的一条链,那么我们考虑\([1, out[x]]\)这个区间,如果对于一个节点\(u\)\(in[u]\)\(out[u]\)都在这个区间里的话,那么说明这个节点\(u\)并不在需要操作的链上。
以链求和为例,我们就可以在平衡树插入的节点进行一些修改。对于节点\(x\),我们在\(in[x]\)出插入\(val[x]\),在\(out[x]\)出插入\(-val[x]\)。那么链求和就是\([1, out[x]]\)中的所有节点的和,这个平衡树也是可以优秀的解决的。唯一不怎么优秀的就是需要开两个平衡树……

这些大概就是一些\(ETT\)能够满足的操作了。可能博主学的并不精,勿喷……

给一道例题吧:例题
代码应该还好写的,就不放出来了……

至于Codechef么……emm……反正比赛也快结束了……\(45\)分的部分分也应该没什么人要的样子啊,我就直接放出来了吧嘿嘿嘿……反正没多少人看……

Code:

用的是\(fhq \ Treap\)来实现的……

大致就是需要支持:\(Link / Cut\)(保证操作的为原树上的边),子树求和,子树赋值,单点加,单点查询,子树加。
还算好写吧……

#include <bits/stdc++.h>
using namespace std;
const int N = 3E5 + 50;
typedef long long ll;
typedef pair<int, int> P;
typedef vector<int> Vec;
#define fi first
#define se second
#define mk make_pair

int n, m, ID, clck;
struct edge {
  int u, v;
}E[N << 2];
ll a[N];
Vec G[N], t;
int in[N], out[N];
char s[N];

namespace Treap {
  struct node {
    int ls, rs, sz, zero, fa;
    ll v, sum, Rnd, tag;
  }t[N << 1];
  int tot, rt; 
#define ls(o) t[o].ls
#define rs(o) t[o].rs
#define pa(o) t[o].fa
  
  int Newnode(ll V) {
    t[++tot].ls = t[tot].rs = t[tot].zero = t[tot].fa = t[tot].tag = 0; t[tot].sz = 1;
    t[tot].Rnd = 1ll * rand() * rand(); t[tot].sum = t[tot].v = V;
    return tot;
  }
  void Update(int o) {
    t[o].sz = 1; t[o].sum = t[o].v;
    t[o].sz += ( ls(o) ? t[ls(o)].sz : 0 ) + ( rs(o) ? t[rs(o)].sz : 0 );
    t[o].sum += ( ls(o) ? t[ls(o)].sum : 0) + ( rs(o) ? t[rs(o)].sum : 0 );
  }

  void Apply1(int o) { t[o].sum = t[o].v = 0; t[o].zero = 1; t[o].tag = 0; }
  void Apply2(int o, ll V) { t[o].sum += t[o].sz * V; t[o].v += V; t[o].tag += V; }
  
  void Push(int o) {
    if(t[o].zero) {
      if(ls(o)) Apply1(ls(o));
      if(rs(o)) Apply1(rs(o));
      t[o].zero = 0;
    }
    if(t[o].tag) {
      if(ls(o)) Apply2(ls(o), t[o].tag);
      if(rs(o)) Apply2(rs(o), t[o].tag);
      t[o].tag = 0;
    }
  }
  
  int Merge(int x, int y) {
    if(!x || !y) return x | y;
    Push(x); Push(y);
    if(t[x].Rnd < t[y].Rnd) {
      int k = Merge(t[x].rs, y);
      t[k].fa = x;
      t[x].rs = k;
      Update(x);
      return x;
    }
    else {
      int k = Merge(x, t[y].ls);
      t[k].fa = y;
      t[y].ls = k;
      Update(y);
      return y;
    }
  }

  P Split(int o, int k) {
    if(!o) return mk(0, 0);
    Push(o);
    if(t[ls(o)].sz == k) {
      int Ls = ls(o);
      ls(o) = 0; t[Ls].fa = 0;
      Update(o);
      return mk(Ls, o);
    }
    if(t[ls(o)].sz + 1 == k) {
      int Rs = rs(o);
      rs(o) = 0; t[Rs].fa = 0;
      Update(o);
      return mk(o, Rs);
    }
    if(t[ls(o)].sz < k) {
      P tmp = Split(rs(o), k - t[ls(o)].sz - 1);
      int Rs = t[o].rs; t[Rs].fa = 0;
      t[o].rs = tmp.fi;
      t[tmp.fi].fa = o;
      Update(o);
      return mk(o, tmp.se);
    }
    P tmp = Split(ls(o), k);
    int Ls = t[o].ls; t[Ls].fa = 0;
    t[o].ls = tmp.se;
    t[tmp.se].fa = o;
    Update(o);
    return mk(tmp.fi, o);
  }

  P Findroot(int o) {
    int las = 0, Rnk = ls(o) ? t[ls(o)].sz : 0;
    while(pa(o)) {
      if(rs(pa(o)) == o) Rnk += t[ls(pa(o))].sz + 1;
      o = pa(o);
    }
    return mk(o, Rnk);
  }

  void Insert(ll V) {
    int o = Newnode(V);
    rt = Merge(rt, o);
  }
  
  void Cut(int x, int y) {
    int L1 = in[x], R1 = out[x], L2 = in[y], R2 = out[y];
    if(L1 <= L2 && R2 <= R1) swap(x, y), swap(L1, L2), swap(R1, R2);
    P L = Findroot(L1), R = Findroot(R1);
    P tmp1 = Split(L.fi, R.se + 1);
    P tmp2 = Split(tmp1.fi, L.se);
    Merge(tmp2.fi, tmp1.se);
  }

  void Link(int x, int y) {
    int L1 = in[x], R1 = out[x], L2 = in[y], R2 = out[y];
    if(L1 <= L2 && R2 <= R1) swap(x, y), swap(L1, L2), swap(R1, R2);
    int o = Findroot(L1).fi;
    P R = Findroot(R2);
    P tmp1 = Split(R.fi, R.se + 1);
    P tmp2 = Split(tmp1.fi, R.se);
    int p = Merge(tmp2.fi, o);
    int k = Merge(p, tmp2.se);
    Merge(k, tmp1.se);
  }
}

void Dfs(int o, int fa) {
  in[o] = ++clck; t.push_back(o);
  for(int i = 0; i < (int)G[o].size(); i++) {
    int to = G[o][i];
    if(to == fa) continue;
    Dfs(to, o);
  }
  out[o] = ++clck; t.push_back(o);
}

int main() {
  Treap::rt = Treap::tot = 0;
  scanf("%d%d%d", &ID, &n, &m);
  if(ID == 4 || ID == 5) return puts("nmdwsm"), 0;
  for(int i = 1, u, v; i < n; i++) {
    scanf("%d%d", &u, &v);
    E[i] = (edge) { u, v };
    G[u].push_back(v); G[v].push_back(u);
  }
  scanf("%s", s + 1);
  for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
  Dfs(1, 1);

  for(int i = 0; i < (int)t.size(); i++) Treap::Insert(a[t[i]]);

  for(int i = 1; i < n; i++) {
    if(s[i] == '0') continue;
    Treap::Cut(E[i].u, E[i].v);
  }

  while(m--) {
    int tp;
    scanf("%d", &tp);
    if(tp == 1) {
      int x;
      scanf("%d", &x);
      if(s[x] == '0') s[x] = '1', Treap::Cut(E[x].u, E[x].v);
      else s[x] = '0', Treap::Link(E[x].u, E[x].v);
    }
    else if(tp == 2) {
      int x; ll y;
      scanf("%d%lld", &x, &y);
      P p = Treap::Findroot(in[x]);
      Treap::Apply2(p.fi, y);
    }
    else if(tp == 3) {
      int x;
      scanf("%d", &x);
      P p = Treap::Findroot(in[x]);
      ll Sum = Treap::t[p.fi].sum;
      Sum >>= 1;
      Treap::Apply1(p.fi);
      P tmp1 = Treap::Split(p.fi, p.se + 1);
      P tmp2 = Treap::Split(tmp1.fi, p.se);
      Treap::t[tmp2.se].sum = Treap::t[tmp2.se].v = Sum;
      Treap::Merge(Treap::Merge(tmp2.fi, tmp2.se), tmp1.se);
      p = Treap::Findroot(out[x]);
      tmp1 = Treap::Split(p.fi, p.se + 1);
      tmp2 = Treap::Split(tmp1.fi, p.se);
      Treap::t[tmp2.se].sum = Treap::t[tmp2.se].v = Sum;
      Treap::Merge(Treap::Merge(tmp2.fi, tmp2.se), tmp1.se);
      
    }
    else if(tp == 4) {
      int x;
      scanf("%d", &x);
      P p = Treap::Findroot(in[x]);
      P tmp1 = Treap::Split(p.fi, p.se + 1);
      P tmp2 = Treap::Split(tmp1.fi, p.se);
      ll ans = Treap::t[tmp2.se].v;
      printf("%lld\n", ans);
      Treap::Merge(Treap::Merge(tmp2.fi, tmp2.se), tmp1.se);
    }
    else if(tp == 5) {
      int x;
      scanf("%d", &x);
      P p = Treap::Findroot(in[x]);
      ll ans = Treap::t[p.fi].sum;
      ans >>= 1;
      printf("%lld\n", ans);
    }
    else if(tp == 6) {
      int x;
      scanf("%d", &x);
      P p = Treap::Findroot(in[x]);
      Treap::Apply1(p.fi);
    }
  }
  return 0;
}
posted @ 2019-04-13 16:38  Apocrypha  阅读(712)  评论(3编辑  收藏  举报