作者: KSkun

Treap原理与实现

Treap原理与实现

注:本文部分图片来自互联网,其相关权利归原作者所有,感谢原作者的分享。

概述

Treap是一种改进的BST(二叉查找树,Binary Search Tree)平衡树,Treap的命名来自于Tree+Heap,其旋转的依据是节点随机权值满足堆序性。通常我们将其规定为小根堆。Treap是常见的平衡树的种类。

原理与实现

我们用结构体Node存放平衡树每个节点的信息,下面是Node的实现。

struct Node {
    int lch, rch, val, rnd, siz, cnt;
    // 从左到右依次是左儿子,右儿子,节点值,随机权值,子树大小,这个数的出现次数
} treap[MAXN]; 

旋转

旋转是Treap的基本操作之一。Treap的旋转分为左旋和右旋两种,如下图所示。
180228a 1 - Treap原理与实现
右旋指将左儿子提到根,将根向下移动到右儿子。左旋是右旋的相反操作。这两种操作有助于保证树的平衡。至于旋转后是否满足堆序性,看旋转操作的过程就可以证明。
下面是左旋和右旋的实现。

inline void lrotate(int &a) {
    int b = treap[a].rch;
    treap[a].rch = treap[b].lch;
    treap[b].lch = a;
    treap[b].siz = treap[a].siz;
    calsiz(a);
    a = b;
}

inline void rrotate(int &a) {
    int b = treap[a].lch;
    treap[a].lch = treap[b].rch;
    treap[b].rch = a;
    treap[b].siz = treap[a].siz;
    calsiz(a);
    a = b;
}

旋转完毕后,a的值就是新的根。

插入

插入的操作如图所示。
180228a 2 - Treap原理与实现
先按照BST的性质(比当前节点的数小的都在左子树,否则在右子树)找到需要插入的位置,然后新建节点放进去。此时,如果发现左右儿子有不满足堆序性的情况,将其旋转至满足堆序性。
下面是插入操作的实现。

inline void insert(int &p, int val) {
    if(!p) {
        p = newnode();
        treap[p].val = val;
        treap[p].rnd = rand();
        treap[p].siz = treap[p].cnt = 1;
        return;
    }
    treap[p].siz++;
    if(treap[p].val == val) {
        treap[p].cnt++;
    } else if(treap[p].val > val) {
        insert(treap[p].lch, val);
        if(treap[treap[p].lch].rnd < treap[p].rnd) rrotate(p); 
    } else {
        insert(treap[p].rch, val);
        if(treap[treap[p].rch].rnd < treap[p].rnd) lrotate(p); 
    }
}

插入操作完成后p会修改为树根。

删除

删除的操作如图所示。
180228a 3 - Treap原理与实现
删除操作首先找到这个要删的点,如果出现次数超过1次直接删出现次数即可。否则需要删除这个点,把这个点转到至多有一棵子树的位置,然后将子树接上来,直接删除即可。
下面是删除操作的实现。

inline void delet(int &p, int val) {
    if(!p) return;
    if(treap[p].val == val) {
        if(treap[p].cnt > 1) {
            treap[p].cnt--;
            treap[p].siz--;
        } else if(!treap[p].lch) {
            delnode(p);
            p = treap[p].rch;
        } else if(!treap[p].rch) {
            delnode(p);
            p = treap[p].lch;
        } else {
            if(treap[treap[p].lch].rnd < treap[treap[p].rch].rnd) {
                rrotate(p);
                delet(p, val);
            } else {
                lrotate(p);
                delet(p, val);
            }
        }  
        return;
    }
    if(treap[p].val > val) {
        treap[p].siz--;
        delet(treap[p].lch, val);
    } else {
        treap[p].siz--;
        delet(treap[p].rch, val); 
    }
}

应用:查询一个数的排名

判断当前节点与查询数的大小关系
大→找左子树
相等→答案为左子树大小+1
小→找右子树,然后加上左子树大小和当前节点出现次数
下面是实现。

inline int queryrk(int p, int val) {
    if(!p) return 0;
    if(treap[p].val == val) return treap[treap[p].lch].siz + 1;
    else if(treap[p].val > val) return queryrk(treap[p].lch, val);
    else return queryrk(treap[p].rch, val) + treap[treap[p].lch].siz + treap[p].cnt;
}

应用:查询排名为某的数

当前节点左子树不大于查询排名→找左子树
查询排名大于当前节点左子树+当前节点出现次数→找右子树
否则→这个数就是我们要找的
下面是实现。

inline int queryn(int p, int rk) {
    if(!p) return 0;
    if(treap[treap[p].lch].siz >= rk) return queryn(treap[p].lch, rk);
    else if(treap[treap[p].lch].siz + treap[p].cnt < rk) return queryn(treap[p].rch, rk - treap[treap[p].lch].siz - treap[p].cnt);
    else return treap[p].val;
}

应用:查询一个数的前驱(不大于这个数的最大数)

当前节点小了→更新答案,找右子树
当前节点大了→找左子树

inline void querypre(int p, int val) {
    if(!p) return;
    if(treap[p].val < val) {
        anst = p;
        querypre(treap[p].rch, val);
    } else return querypre(treap[p].lch, val);
}

应用:查询一个数的后继(不小于这个数的最小数)

与上面的查询相似。

inline void querynxt(int p, int val) {
    if(!p) return;
    if(treap[p].val > val) {
        anst = p;
        querynxt(treap[p].lch, val);
    } else return querynxt(treap[p].rch, val);
}

代码

这份代码可以通过洛谷【P3369】【模板】普通平衡树(Treap/SBT) – 洛谷或BZOJProblem 3224. — Tyvj 1728 普通平衡树(BZOJ不能使用ctime库)题目。

// Code by KSkun, 2018/2
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    char c = fgc(); 
    while (c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while (c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

// variable

const int MAXN = 100005, INF = 1e9;

int n, op, x;

// treap

struct Node {
    int lch, rch, val, rnd, siz, cnt;
} treap[MAXN]; 
int tot = 0, sta[MAXN], stop = 0, rt = 0, anst;

inline void calsiz(int p) {
    treap[p].siz = treap[treap[p].lch].siz + treap[treap[p].rch].siz + treap[p].cnt;
}

inline int newnode() {
    int p;
    if(stop > 0) {
        p = sta[--stop];
    } else {
        p = ++tot;
    } 
    memset(treap + p, 0, sizeof(Node));
    return p;
}

inline void delnode(int a) {
    sta[stop++] = a;
}

inline void lrotate(int &a) {
    int b = treap[a].rch;
    treap[a].rch = treap[b].lch;
    treap[b].lch = a;
    treap[b].siz = treap[a].siz;
    calsiz(a);
    a = b;
}

inline void rrotate(int &a) {
    int b = treap[a].lch;
    treap[a].lch = treap[b].rch;
    treap[b].rch = a;
    treap[b].siz = treap[a].siz;
    calsiz(a);
    a = b;
}

inline void insert(int &p, int val) {
    if(!p) {
        p = newnode();
        treap[p].val = val;
        treap[p].rnd = rand();
        treap[p].siz = treap[p].cnt = 1;
        return;
    }
    treap[p].siz++;
    if(treap[p].val == val) {
        treap[p].cnt++;
    } else if(treap[p].val > val) {
        insert(treap[p].lch, val);
        if(treap[treap[p].lch].rnd < treap[p].rnd) rrotate(p); 
    } else {
        insert(treap[p].rch, val);
        if(treap[treap[p].rch].rnd < treap[p].rnd) lrotate(p); 
    }
}

inline void delet(int &p, int val) {
    if(!p) return;
    if(treap[p].val == val) {
        if(treap[p].cnt > 1) {
            treap[p].cnt--;
            treap[p].siz--;
        } else if(!treap[p].lch) {
            delnode(p);
            p = treap[p].rch;
        } else if(!treap[p].rch) {
            delnode(p);
            p = treap[p].lch;
        } else {
            if(treap[treap[p].lch].rnd < treap[treap[p].rch].rnd) {
                rrotate(p);
                delet(p, val);
            } else {
                lrotate(p);
                delet(p, val);
            }
        }  
        return;
    }
    if(treap[p].val > val) {
        treap[p].siz--;
        delet(treap[p].lch, val);
    } else {
        treap[p].siz--;
        delet(treap[p].rch, val); 
    }
}

inline int queryrk(int p, int val) {
    if(!p) return 0;
    if(treap[p].val == val) return treap[treap[p].lch].siz + 1;
    else if(treap[p].val > val) return queryrk(treap[p].lch, val);
    else return queryrk(treap[p].rch, val) + treap[treap[p].lch].siz + treap[p].cnt;
}

inline int queryn(int p, int rk) {
    if(!p) return 0;
    if(treap[treap[p].lch].siz >= rk) return queryn(treap[p].lch, rk);
    else if(treap[treap[p].lch].siz + treap[p].cnt < rk) return queryn(treap[p].rch, rk - treap[treap[p].lch].siz - treap[p].cnt);
    else return treap[p].val;
}

inline void querypre(int p, int val) {
    if(!p) return;
    if(treap[p].val < val) {
        anst = p;
        querypre(treap[p].rch, val);
    } else return querypre(treap[p].lch, val);
}

inline void querynxt(int p, int val) {
    if(!p) return;
    if(treap[p].val > val) {
        anst = p;
        querynxt(treap[p].lch, val);
    } else return querynxt(treap[p].rch, val);
}

int main() {
    srand(time(NULL));
    n = readint();
    while(n--) {
        op = readint();
        x = readint();
        switch(op) {
            case 1:
                insert(rt, x);
                break;
            case 2:
                delet(rt, x);
                break;
            case 3:
                printf("%d\n", queryrk(rt, x));
                break;
            case 4:
                printf("%d\n", queryn(rt, x));
                break;
            case 5:
                anst = 0;
                querypre(rt, x);
                printf("%d\n", treap[anst].val);
                break;
            case 6:
                anst = 0;
                querynxt(rt, x);
                printf("%d\n", treap[anst].val);
                break; 
        } 
    }
    return 0;
}
[BZOJ3938]Robot 题解

[BZOJ3938]Robot 题解

题目地址:BZOJ:Problem 3938. — Robot

题目描述

小q有n只机器人,一开始他把机器人放在了一条数轴上,第i只机器人在ai的位置上静止,而自己站在原点。在这之后小q会执行一些操作,他想要命令一个机器人向左或者向右移动x格。但是机器人似乎听不清小q的命令,事实上它们会以每秒x格的速度匀速移动。看着自己的机器人越走越远,小q很着急,他想知道当前离他(原点)最远的机器人有多远。具体的操作以及询问见输入格式。注意,不同的机器人之间互不影响,即不用考虑两个机器人撞在了一起的情况。

输入输出格式

输入格式:
共有m个事件,输入将会按事件的时间顺序给出。第一行两个正整数n,m。接下来一行n个整数,第i个数是ai,表示第i个机器人初始的位置(初始移动速度为0)。接下来m行,每行行首是一个非负整数ti,表示该事件点发生的时刻(以秒为单位)。第二个是一个字符串S,代表操作的种类。数字与字符串之间用一个空格隔开。接下来的输入按S的种类分类。若S是command(不带引号),则接下来两个整数ki,xi,表示小q对第ki个机器人执行了操作,该机器人的速度将会被重置,变为向数轴正方向每秒移动xi格(若xi为负数就相当于向数轴负方向每秒移动∣xi∣格)。保证1≤ki≤n。若S是query(不带引号),则你需要输出当前离原点最远的机器人有多远。保证t1≤t2≤t2≤…≤tm。(注:若同一时间发生多次操作,则按读入顺序依次执行)

输出格式:
对于每个query询问,输出一行,包含一个整数表示正确的答案。C/C++输入输出long long时请用%lld。由于本题数据量较大,建议不要使用cin/cout进行输入输出。

输入输出样例

输入样例#1:

4 5
-20 0 20 100
10 command 1 10
20 command 3 -10
30 query
40 command 1 -30
50 query

输出样例#1:

180
280

说明

样例说明:
第一个命令执行时,各个机器人的位置为:−20,0,20,100。
第二个命令执行时,各个机器人的位置为:80,0,20,100。
第一个询问时,各个机器人的位置为:180,0,−80,100。
第三个命令执行时,各个机器人的位置为:280,0,−180,100。
第二个询问时,各个机器人的位置为:−20,0,−280,100。

数据范围:
command 的个数为 C,query 的个数为 Q。(所以 C+Q=m)
对于所有的事件满足0 \leq t_i \leq 10^9,对于所有的 command 满足 |x_i| \leq 10^4
对于所有的机器人满足|a_i| \leq 10^9
N, C \leq 10^5
Q \leq 5 \times 10^5

题解

首先考虑机器人的位移-时间图像一定是折线图,我们把折线拆成线段,加入标记永久化线段树里。考虑离线处理需要加入的线段,并且离散化时间。
关于标记永久化线段树存入线段的用法,这个题是一个入门题:[JSOI2008]Blue Mary开公司 题解 | KSkun’s Blog,本题也需要像这个题一样画图来观察直线间的关系,只不过那个题斜率一定为正,这个题斜率可以为负。
对于每一个询问,需要找到该询问处的最大值和最小值,因为位移可为负数,取绝对值后也可以很大。正因如此,我们需要两棵线段树,一棵存最大值,一棵存最小值。

代码

// Code by KSkun, 2018/2
#include <cstdio>
#include <vector> 
#include <algorithm>
typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc(); 
    while (c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while (c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXQ = 600005, MAXN = 100005;

inline bool isop(char c) {
    return c == 'c' || c == 'q';
}

inline char readop() {
    char c = fgc();
    while (!isop(c)) c = fgc();
    return c;
}

// global variables

struct Line {
    LL k, b;
    Line() {}
    Line(LL k, LL b): k(k), b(b) {} 
    inline LL cal(LL x) {
        return k * x + b; 
    }
};

Line lstl[MAXN];
LL n, m, N, lstt[MAXN];

struct Query {
    LL time, x, k;
    char op;
} ques[MAXQ];

inline bool cmp(Line l1, Line l2, LL x) { // if l1 bigger than l2 on pos x
    return l1.cal(x) > l2.cal(x);
} 

inline double interx(Line l1, Line l2) {
    return double(l2.b - l1.b) / (l1.k - l2.k);
}

std::vector<LL> tmp; 

// seg tree

#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)

Line tmx[MAXQ << 2], tmn[MAXQ << 2];
bool imx[MAXQ << 2], imn[MAXQ << 2];

inline void addmx(int o, int l, int r, int ll, int rr, Line line) {
    if(l >= ll && r <= rr) {
        if(!imx[o]) {
            tmx[o] = line;
            imx[o] = true;
            return;
        }
        LL olv = tmx[o].cal(tmp[l]), orv = tmx[o].cal(tmp[r]), nlv = line.cal(tmp[l]), nrv = line.cal(tmp[r]);
        if(olv >= nlv && orv >= nrv) {
            return;
        }
        if(olv <= nlv && orv <= nrv) {
            tmx[o] = line;
            return;
        }
        double ix = interx(tmx[o], line);
        if(olv >= nlv) {
            if(ix <= tmp[mid]) {
                addmx(lch, l, mid, ll, rr, tmx[o]);
                tmx[o] = line;
            } else {
                addmx(rch, mid + 1, r, ll, rr, line);
            }
        } else {
            if(ix > tmp[mid]) {
                addmx(rch, mid + 1, r, ll, rr, tmx[o]);
                tmx[o] = line;
            } else {
                addmx(lch, l, mid, ll, rr, line);
            }
        }
        return;
    }
    if(ll <= mid) addmx(lch, l, mid, ll, rr, line);
    if(rr > mid) addmx(rch, mid + 1, r, ll, rr, line);
}

inline void addmn(int o, int l, int r, int ll, int rr, Line line) {
    if(l >= ll && r <= rr) {
        if(!imn[o]) {
            tmn[o] = line;
            imn[o] = true;
            return;
        }
        LL olv = tmn[o].cal(tmp[l]), orv = tmn[o].cal(tmp[r]), nlv = line.cal(tmp[l]), nrv = line.cal(tmp[r]);
        if(olv <= nlv && orv <= nrv) {
            return;
        }
        if(olv >= nlv && orv >= nrv) {
            tmn[o] = line;
            return;
        }
        double ix = interx(tmn[o], line);
        if(olv < nlv) {
            if(ix <= tmp[mid]) {
                addmn(lch, l, mid, ll, rr, tmn[o]);
                tmn[o] = line;
            } else {
                addmn(rch, mid + 1, r, ll, rr, line);
            }
        } else {
            if(ix > tmp[mid]) {
                addmn(rch, mid + 1, r, ll, rr, tmn[o]);
                tmn[o] = line;
            } else {
                addmn(lch, l, mid, ll, rr, line);
            }
        }
        return;
    }
    if(ll <= mid) addmn(lch, l, mid, ll, rr, line);
    if(rr > mid) addmn(rch, mid + 1, r, ll, rr, line);
}

inline LL quemx(int o, int l, int r, int x) {
    LL res = -1e15;
    if(imx[o]) res = std::max(res, tmx[o].cal(tmp[x]));
    if(l == r) return res;
    if(x <= mid) res = std::max(res, quemx(lch, l, mid, x));
    if(x > mid) res = std::max(res, quemx(rch, mid + 1, r, x));
    return res;
}

inline LL quemn(int o, int l, int r, int x) {
    LL res = 1e15;
    if(imn[o]) res = std::min(res, tmn[o].cal(tmp[x]));
    if(l == r) return res;
    if(x <= mid) res = std::min(res, quemn(lch, l, mid, x));
    if(x > mid) res = std::min(res, quemn(rch, mid + 1, r, x));
    return res;
}

// main

int main() {
    n = readint();
    m = readint();
    for(int i = 1; i <= n; i++) {
        lstl[i].b = readint();
    }
    for(int i = 1; i <= m; i++) {
        ques[i].time = readint();
        ques[i].op = readop();
        if(ques[i].op == 'c') {
            ques[i].x = readint();
            ques[i].k = readint();
        }
        tmp.push_back(ques[i].time);
    }
    tmp.push_back(-1);
    tmp.push_back(0);
    std::sort(tmp.begin(), tmp.end());
    tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
    N = tmp.size() - 1;
    for(int i = 1; i <= m; i++) {
        if(ques[i].op == 'c') {
            LL x = ques[i].x, 
                lst = std::lower_bound(tmp.begin(), tmp.end(), lstt[x]) - tmp.begin(), 
                now = std::lower_bound(tmp.begin(), tmp.end(), ques[i].time) - tmp.begin();
            addmx(1, 1, N, lst, now, lstl[x]);
            addmn(1, 1, N, lst, now, lstl[x]);
            lstl[x] = Line(ques[i].k, lstl[x].b + ques[i].time * (lstl[x].k - ques[i].k));
            lstt[x] = ques[i].time; 
        }
    }
    for(int i = 1; i <= n; i++) {
        LL lst = std::lower_bound(tmp.begin(), tmp.end(), lstt[i]) - tmp.begin();
        addmx(1, 1, N, lst, N, lstl[i]);
        addmn(1, 1, N, lst, N, lstl[i]);
    }
    for(int i = 1; i <= m; i++) {
        if(ques[i].op == 'q') {
            LL now = std::lower_bound(tmp.begin(), tmp.end(), ques[i].time) - tmp.begin(),
                ans1 = quemx(1, 1, N, now), ans2 = -quemn(1, 1, N, now);
            printf("%lld\n", std::max(ans1, ans2));
        }
    }
    return 0;
}
[SHOI2008]堵塞的交通 题解

[SHOI2008]堵塞的交通 题解

题目地址:洛谷:【P4246】[SHOI2008]堵塞的交通 – 洛谷、BZOJ:Problem 1018. — [SHOI2008]堵塞的交通traffic

题目描述

有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个城市和3C-2条道路。

小人国的交通状况非常槽糕。有的时候由于交通堵塞,两座城市之间的道路会变得不连通,直到拥堵解决,道路才会恢复畅通。初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入膏肓的小人国交通系统。 小人国的交通部将提供一些交通信息给你,你的任务是根据当前的交通情况回答查询的问题。交通信息可以分为以下几种格式:

  • Close r1 c1 r2 c2:相邻的两座城市(r1, c1)和(r2, c2)之间的道路被堵塞了;
  • Open r1 c1 r2 c2:相邻的两座城市(r1, c1)和(r2, c2)之间的道路被疏通了;
  • Ask r1 c1 r2 c2:询问城市(r1, c1)和(r2, c2)是否连通。如果存在一条路径使得这两条城市连通,则返回Y,否则返回N

注:ri表示行数,ci表示列数,1 \leq r_i \leq 2, 1 \leq c_i \leq C

输入输出格式

输入格式:
第一行只有一个整数C,表示网格的列数。接下来若干行,每行为一条交通信息,以单独的一行Exit作为结束。我们假设在一开始所有的道路都是堵塞的。我们保证C小于等于100000,信息条数小于等于100000。

输出格式:
对于每个查询,输出一个YN

输入输出样例

输入样例#1:

2
Open 1 1 1 2
Open 1 2 2 2
Ask 1 1 2 2
Ask 2 1 2 2
Exit

输出样例#1:

Y
N

说明

数据范围:
对于100%的数据,1 \leq C \leq 1000001 \leq信息条数\leq 100000

题解

设计可合并的状态

本题是一个线段树维护连通性的题目,那么必然要设计区间信息的合并,设计一个可合并的状态很有必要。这里我的设计是对于区间[l, r]存储(1, l)、(2, l)、(1, r)、(2, r)四个点两两间的连通性,总共有6种(即左上左下右上右下)。另外把横向道路放在线段树外面管理。
合并的时候,我们分情况讨论。例如需要维护左下右上这一情况,可以走的路线一共两种,如下图。
180223a 1 - [SHOI2008]堵塞的交通 题解
将六种情况都处理完毕合并就完成了。

修改

竖向道路:找到包含这条道路的长度为1的区间(即[x, x]),修改该区间内四种状态(左上右上、左下右下永远连通)即可。
横向道路:线段树递归的时候,发现有一个区间的mid和mid+1恰好是横向道路两端的c坐标时,在外部维护的横向道路修改后更新这个区间的信息,因为横向道路的影响是从这个区间开始产生的。

查询

直接查[c1, c2]?显然不行,因为有下面这种情况:
180223a 2 - [SHOI2008]堵塞的交通 题解
不一定直接能到,也许要往左往右绕,所以我们还得考虑绕的情况。除了[c1, c2]以外,还要把[1, c1]、[c2, C]两个区间的信息拿到,查c1上下两点,c2上下两点,再查这4个点之间的连通情况。

总结

线段树维护连通性,很新奇的用法。这道题的思维难度并不大,但是实现难度不小。主要细节集中于合并区间信息与查询答案的时候。写这个题的时候,用别人题解里的程序拍了好久才改出来。总之是很烦人的就对了。

代码

注:区间信息中,lurd是左(left)上(up)右(right)上(down)的意思,其他的以此类推。ru数组是上方(up)的横向道路(road),rd以此类推。

// Code by KSkun, 2018/2
#include <cstdio>
#include <cstring>
#include <algorithm>

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF 
            : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    char c = fgc(); 
    while (c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while (c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 100005;

int c, r1, c1, r2, c2;
char op;

inline bool isop(char c) {
    return c == 'O' || c == 'C' || c == 'A' || c == 'E';
}

inline char readop() {
    char c = fgc();
    while (!isop(c)) c = fgc();
    return c;
}

#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)

bool ru[MAXN], rd[MAXN];

struct Data {
    int l, r;
    bool luru, ldrd, luld, rurd, lurd, ldru;
} tree[MAXN << 2];

inline void merge(Data *rt, Data ls, Data rs) { // 合并区间信息 
    rt->l = ls.l;
    rt->r = rs.r;
    rt->luru = (ls.luru && ru[ls.r] && rs.luru) || (ls.lurd && rd[ls.r] && rs.ldru); // 左上右上
    rt->lurd = (ls.luru && ru[ls.r] && rs.lurd) || (ls.lurd && rd[ls.r] && rs.ldrd); // 左上右下
    rt->ldru = (ls.ldru && ru[ls.r] && rs.luru) || (ls.ldrd && rd[ls.r] && rs.ldru); // 左下右上
    rt->ldrd = (ls.ldru && ru[ls.r] && rs.lurd) || (ls.ldrd && rd[ls.r] && rs.ldrd); // 左下右下
    rt->luld = ls.luld || (ls.luru && ru[ls.r] && rs.luld && rd[ls.r] && ls.ldrd); // 左上左下
    rt->rurd = rs.rurd || (rs.luru && ru[ls.r] && ls.rurd && rd[ls.r] && rs.ldrd); // 右上右下
} 

inline void build(int o, int l, int r) {
    if (l == r) {
        tree[o].l = tree[o].r = l;
        tree[o].luru = tree[o].ldrd = true;
        return;
    }
    build(lch, l, mid);
    build(rch, mid + 1, r);
    merge(&tree[o], tree[lch], tree[rch]);
}

inline void modify1(int o, int l, int r, int x, bool comm) {
    if (l == r) {
        tree[o].luld = tree[o].rurd = tree[o].lurd = tree[o].ldru = comm;
        return;
    }
    if (x <= mid) modify1(lch, l, mid, x, comm);
    if (x > mid) modify1(rch, mid + 1, r, x, comm);
    merge(&tree[o], tree[lch], tree[rch]);
}

inline void modify2(int o, int l, int r, int x, bool up, bool comm) {
    if (mid == x) {
        if(up) ru[x] = comm; else rd[x] = comm;
        merge(&tree[o], tree[lch], tree[rch]);
        return;
    }
    if (x <= mid) modify2(lch, l, mid, x, up, comm);
    if (x > mid) modify2(rch, mid + 1, r, x, up, comm);
    merge(&tree[o], tree[lch], tree[rch]);
}

inline Data query(int o, int l, int r, int ll, int rr) {
    if (l >= ll && r <= rr) {
        return tree[o];
    }
    if (rr <= mid) return query(lch, l, mid, ll, rr);
    else if (ll > mid) return query(rch, mid + 1, r, ll, rr);
    else {
        Data res;
        merge(&res, query(lch, l, mid, ll, mid), query(rch, mid + 1, r, mid + 1, rr));
        return res;
    }
}

int main() {
    c = readint();
    build(1, 1, c);
    for (;;) {
        op = readop();
        if (op == 'E') break;
        r1 = readint();
        c1 = readint();
        r2 = readint();
        c2 = readint();
        if (c1 > c2) {
            std::swap(r1, r2);
            std::swap(c1, c2);
        }
        if (op == 'O') {
            if (r1 == r2) {
                modify2(1, 1, c, c1, r1 == 1, true);
            } else {
                modify1(1, 1, c, c1, true);
            }
        }
        if (op == 'C') {
            if (r1 == r2) {
                modify2(1, 1, c, c1, r1 == 1, false);
            } else {
                modify1(1, 1, c, c1, false);
            }
        }
        if (op == 'A') {
            Data rm = query(1, 1, c, c1, c2), rl = query(1, 1, c, 1, c1), 
                rr = query(1, 1, c, c2, c);
            bool b1, b2, b3, b4;
            if (r1 == 1 && r2 == 1) {
                b1 = rm.luru;
                b2 = rl.rurd && rm.ldru;
                b3 = rr.luld && rm.lurd;
                b4 = rl.rurd && rm.ldrd && rr.luld;
                if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
            } else if (r1 == 1 && r2 == 2) {
                b1 = rm.lurd;
                b2 = rl.rurd && rm.ldrd;
                b3 = rr.luld && rm.luru;
                b4 = rl.rurd && rm.ldru && rr.luld;
                if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
            } else if (r1 == 2 && r2 == 1) {
                b1 = rm.ldru;
                b2 = rl.rurd && rm.luru;
                b3 = rr.luld && rm.ldrd;
                b4 = rl.rurd && rm.lurd && rr.luld;
                if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
            } else {
                b1 = rm.ldrd;
                b2 = rl.rurd && rm.lurd;
                b3 = rr.luld && rm.ldru;
                b4 = rl.rurd && rm.luru && rr.luld;
                if(b1 || b2 || b3 || b4) printf("Y\n"); else printf("N\n");
            }
        }
    }
    return 0;
}