spoj61

spoj61

写个总结吧, 因为挺多细节的



我们可以这样考虑, 设
a[i] = 1 如果 第i个字符是 (
     = -1 如果第i个字符是 )


然后设 src[i] = a[1] + a[2] + ... + a[i]
可以证明, 如果一个括号序列是合法的话, 那么 src[1], src[2] .. src[N] 都是大于等于 0的
但是小心, 这还是不足够的, 这只是必要条件
其次, 如果一个括号序列是合法的话, src[N] 必定等于 0


综合起来就是, 一个合法的括号序列 等价于 
src[1...N] 全部都大于等于 0 并且 src[N] 等于 0


这下子, 开始跟区间树有联系了.


继续看, 如果我们把第i个字符"取反", 譬如说, 
将 ( 变成 ), 那么, 得到的效果就是, src[i] , src[i + 1] ...src[N] 每一个都 减去2
将 ) 变成 (, 那么, 得到的效果就是, src[i] , src[i + 1] ...src[N] 每一个都 加上2


于是, 我们可以将这个题目跟区间树联系起来了, 
首先, 我们由原始的括号序列str[1..N]得到一个src[1..N]数组
然后, 我们要在这个数组上做两个操作:
成段更新: 将某段src[k..N]全部都加上一个数
查询: 查询src[1..N]是否存在负数




但是我们会发现, 如果我们直接在节点上使用一个字段来标志 该区间是否存在负数的话, 是不能区间更新的, 所以, 我们具体化, 记录 该区间上的最小值
也就是说, 对于节点p, p->min记录的是p对应的区间上的最小值


然后, 问题就解决了.

#include <cstdio>
using namespace std;

char str[30000 + 10];
int N;
int src[30000 + 10];


struct _Node {
  _Node * left,  * right;
  int min;
  int add;

};


typedef _Node * NodePtr;

template<class T>
inline T getMin(T & a, T & b) { return a < b ? a : b; }

_Node __nodelist[(30000 + 10) * 4];
int __top = 0;

NodePtr newNode() {
  return &(__nodelist[__top ++]);
}



NodePtr makeTree(int a, int z) {
  if (a == z) {
    NodePtr cur = newNode();
    cur->left = cur->right = NULL;
    cur->min = src[a];
    cur->add = 0;

    return cur;
  }

  int mid = a + (z -a ) / 2;
  NodePtr cur = newNode();
  cur->left = makeTree(a, mid);
  cur->right = makeTree(mid + 1, z);
  cur->min = getMin(cur->left->min, cur->right->min);
  cur->add = 0;

  return cur;
}

void pushDown(NodePtr cur) {
  if (cur == NULL) return;
  if (cur->add == 0) return;
  cur->min = cur->min + cur->add;
  if (cur->left)
    cur->left->add += cur->add;
  if (cur->right)
    cur->right->add += cur->add;

  cur->add = 0;
  return;
}

inline bool common(int a, int b, int c, int d) {
  return !(b < c || d < a);
}


void change(NodePtr cur, int a, int z, int start, int end, int val) {
  //只要进入一个节点就要pushDown一次
  pushDown(cur);
  if (start <= a && z <= end) {
    cur->add = val;
    pushDown(cur);
    return;
  }

  int mid = a + (z -a ) / 2;
  if (common(a, mid, start, end))
    change(cur->left, a, mid, start, end, val);
  if (common(mid + 1, z, start, end))
    change(cur->right, mid + 1, z, start, end, val);


  //注意下面的两个pushDown, 是非常容易遗漏的, 如果不写, 想想会有什么问题, 呵呵
  pushDown(cur->left);
  pushDown(cur->right);
  cur->min = getMin(cur->left->min, cur->right->min);


  return;

}



NodePtr getNode(NodePtr cur, int a, int z, int pos) {
  //只要进入一个节点就要pushDown一次
  pushDown(cur);
  if (a == z && a == pos) return cur;
  int mid = a + (z -a ) / 2;
  if (pos <= mid) return getNode(cur->left, a, mid, pos);
  else return getNode(cur->right, mid + 1, z, pos);
}

NodePtr root;



void run() {
  scanf("%d", &N);
  scanf("%s", str + 1);
  int i;
  src[0] = 0;
  for (i = 1; str[i]; ++i) {
    if (str[i] == '(') {
      src[i] = src[i - 1] + 1;
    } else {
      src[i] = src[i - 1] - 1;
    }
  }

  __top = 0;
  root = makeTree(1, N);

  int M;
  scanf("%d", &M);

  for (i = 1; i <= M; ++i) {
    int x;
    scanf("%d", &x);
    if (x == 0) {
      pushDown(root);
      //printf("root->min %d\n", root->min);
      if ( root->min >= 0 && (getNode(root, 1, N, N)->min == 0))
        printf("YES\n");
      else
        printf("NO\n");
    } else {
      if (str[x] == '(') {
        str[x] = ')';
        change(root, 1, N, x, N, -2);
      } else {
        str[x] = '(';
        change(root, 1, N, x, N, 2);
      }
    }
  }
}


int curCase;

int main() {
  for (curCase = 1; curCase <= 10; ++curCase) {
    printf("Test %d:\n", curCase);
    run();

  }

  return 0;
}

你可能感兴趣的:(spoj61)