1969: #503. 「LibreOJ β Round」ZQC 的课堂

文章讲述了如何运用树状数组技术解决关于向量移动导致的线段穿越坐标轴的问题,涉及操作符B/F/C的处理和查询算法
摘要由CSDN通过智能技术生成

题目描述

叮铃铃 …… 上课铃响了。

「啊,又是无聊的 math」,坐在教室里的 ZQC 这样想道。Mr.Sam 今天在课上讲了平面直角坐标系上的向量。「这不是幼儿园姿势么」,ZQC 实在忍不住,睡着了。Mr.Sam 把 ZQC 给叫醒,并给了他这样一道题:

假设有一平面直角坐标系,ZQC 有一支画笔,起点是 (1,1) (1, 1)(1,1),现在有 nnn 个向量,第 iii 个向量形如 (xi,yi) (x_i, y_i)(xi,yi),且满足每一个向量都满足 xi,yi x_i, y_ixi,yi 均为偶数。ZQC 按顺序根据这些向量改变自己的画笔的位置,即位置依次改变成 (1+x1,1+y1),(1+x1+x2,1+y1+y2)… (1 + x_1, 1 + y_1), (1 + x_1 + x_2, 1 + y_1 + y_2) \ldots(1+x1,1+y1),(1+x1+x2,1+y1+y2)…。每次改变位置时,画笔都沿两点之间的最短距离移动。结束时,画笔的运动轨迹一定由 nnn 条线段组成。Mr.Sam 要 ZQC 回答这些线段穿过 xxx 轴和 yyy 轴的总次数之和是多少。

但这样的问题对 ZQC 来说太简单了,于是 Mr.Sam 设定了一个指针,一开始指在第一个向量。现在他做了 q(q≤3×105) q(q \leq 3 \times 10 ^ 5)q(q≤3×105) 个操作,操作有四种,分别是:

  1. B 表示把指针向后移动,如果越界则视为无效。即,如果设指针移动前的位置是 iii,那么移动后的位置是 max(1,i−1)\max(1,i-1)max(1,i−1)。
  2. F 表示把指针向前移动,如果越界则视为无效。即,如果设指针移动前的位置是 iii,那么移动后的位置是 min(n,i+1)\min(n,i+1)min(n,i+1)。
  3. C nx ny 把当前指针所指的向量修改为 (nx,ny)(\text{nx},\text{ny})(nx,ny),这里同样满足 nx,ny\text{nx},\text{ny}nx,ny 为偶数。
  4. Q 假设 ZQC 从起点开始,按第 1 11 个到第 n(n≤105) n(n \leq 10 ^ 5)n(n≤105) 个的顺序沿向量走,询问画出的 nnn 条线段穿过 xxx 轴和 yyy 轴次数的总和。

ZQC 想了想,这不是思博题么。

我是要拿图灵奖和菲尔兹奖的男人,这种题浪费我时间,不做!

但是如果不做的话,ZQC 又会遭到 detention,所以他希望聪明的你能在 +1s 内帮他解决这道题。

输入

第一行一个正整数 n nn。
接下来 n nn 行每行两个整数 x,y x, yx,y,保证 x,y x, yx,y 均为偶数。
接下来一行一个整数 q qq。
接下来 q qq 行,格式见「题目描述」。

输出

对于询问中的每个 q qq,输出画出的 n nn 条线段穿过 x xx 轴和 y yy 轴次数的总和。

样例输入 

6
2 2
2 -6
-2 -4
-6 4
10 -10
-8 12
16
Q
C -4 -4 
F
F
Q
F
C 6 -2 
B
B
B
Q
C 0 6 
Q
F
C -8 4 
Q

样例输出 

4
4
3
1
5

提示

题目中出现的坐标值的绝对值均不超过 500 500500。

因为起点是 (1,1)(1,1)(1,1) 而每个向量的每个分量均为偶数,故每次画笔停留的位置横纵坐标均为奇数,不可能在坐标轴上。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
inline void read(int &x){
    x=0;static char ch;static bool flag;flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
#define mk(x,y) make_pair(x,y)
#define fst first
#define snd second
const int maxn = 100010;
struct Treap{
    struct Node{
        Node *ch[2];
        int w,siz,fix;
        void update(){
            siz = ch[0]->siz + ch[1]->siz + 1;
        }
    }*root,mem[maxn],*it,*null;
    Node *ws[maxn];int top;
    inline void init(){
        it = mem;null = it ++ ;
        null->ch[0] = null->ch[1] = null;
        null->siz = 0;null->fix = 0x3f3f3f3f;
        root = null;
    }
    inline Node* newNode(int x){
        Node *p = top ? ws[top--] : it ++ ;
        p->ch[0] = p->ch[1] = null;
        p->siz = 1;p->fix = rand();
        p->w = x;return p;
    }
    inline void rotate(Node* &p,int k){
        Node *x = p->ch[k^1];
        p->ch[k^1] = x->ch[k];
        x->ch[k] = p;
        p->update();x->update();
        p = x;return ;
    }
    void insert(Node* &p,int x){
        if(p == null){
            p = newNode(x);
            return ;
        }insert(p->ch[x >= p->w],x);
        if(p->ch[x>=p->w]->fix < p->fix)
            rotate(p,x<p->w);
        p->update();
    }
    void erase(Node* &p,int x){
        if(p->w == x){
            if(p->ch[0] != null && p->ch[1] != null){
                int k = p->ch[0]->fix < p->ch[1]->fix;
                rotate(p,k);
                erase(p->ch[k],x);
            }else{
                Node *y = p->ch[0] != null ? p->ch[0] : p->ch[1];
                ws[++top] = p;p = y;
            }
        }else erase(p->ch[x >= p->w],x);
        if(p != null) p->update();
    }
    inline void insert(int x){insert(root,x);}
    inline void erase(int x){erase(root,x);}
    inline int les(int x){
        Node *p = root;
        int res = 0;
        while(p != null){
            if(p->w > x) p = p->ch[0];
            else res += p->ch[0]->siz + 1,p = p->ch[1];
        }return res;
    }
    inline int gre(int x){
        Node *p = root;
        int res = 0;
        while(p != null){
            if(p->w < x) p = p->ch[1];
            else res += p->ch[1]->siz + 1,p = p->ch[0];
        }return res;
    }
    Treap(){init();}
}zsx[2],zsy[2];
pa a[maxn];
int X,Y,dx,dy,pot,n,ansx,ansy;
inline void move_right(){
    if(pot == n) return ;++ pot;
    zsx[0].erase(min(X-dx,X+a[pot].fst-dx));
    zsy[0].erase(min(Y-dy,Y+a[pot].snd-dy));
    zsx[1].erase(max(X-dx,X+a[pot].fst-dx));
    zsy[1].erase(max(Y-dy,Y+a[pot].snd-dy));
    if(X*(X+a[pot].fst) < 0) ++ ansx;
    if(Y*(Y+a[pot].snd) < 0) ++ ansy;
    X += a[pot].fst;Y += a[pot].snd;
}
inline void move_left(){
    if(pot == 1) return ;
    X -= a[pot].fst;Y -= a[pot].snd;
    if(X*(X+a[pot].fst) < 0) -- ansx;
    if(Y*(Y+a[pot].snd) < 0) -- ansy;
    zsx[0].insert(min(X-dx,X+a[pot].fst-dx));
    zsy[0].insert(min(Y-dy,Y+a[pot].snd-dy));
    zsx[1].insert(max(X-dx,X+a[pot].fst-dx));
    zsy[1].insert(max(Y-dy,Y+a[pot].snd-dy));
    -- pot;
}
inline void modify(){
    int x,y;read(x);read(y);
    if(X*(X-a[pot].fst) < 0) -- ansx;
    if(Y*(Y-a[pot].snd) < 0) -- ansy;
    X += x - a[pot].fst;
    Y += y - a[pot].snd;
    dx += x - a[pot].fst;
    dy += y - a[pot].snd;
    a[pot] = mk(x,y);
    if(X*(X-x) < 0) ++ ansx;
    if(Y*(Y-y) < 0) ++ ansy;
}
inline int query(){
    int res = n - pot - zsx[0].gre(-dx) - zsx[1].les(-dx) + 
        n - pot - zsy[0].gre(-dy) - zsy[1].les(-dy);
    return res + ansx + ansy;
}
int main(){
    srand(2619520);
    int q;read(n);
    X = Y = 1;
    rep(i,1,n){
        read(a[i].fst);read(a[i].snd);
        zsx[0].insert(min(X,X+a[i].fst));
        zsy[0].insert(min(Y,Y+a[i].snd));
        zsx[1].insert(max(X,X+a[i].fst));
        zsy[1].insert(max(Y,Y+a[i].snd));
        X += a[i].fst;Y += a[i].snd;
    }X = Y = 1;
    move_right();read(q);
    char ch;
    while(q--){
        while(ch=getchar(),ch<'!');
        if(ch == 'B') move_left();
        else if(ch == 'F') move_right();
        else if(ch == 'C') modify();
        else printf("%d\n",query());
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值