Gym 102028C - Supreme Command - [思惟題][2018-2019 ACM-ICPC Asia Jiaozuo Regional Contest Problem C]

題目連接:https://codeforces.com/gym/102028/problem/Cc++

Lewis likes playing chess. Now he has n rooks on the chessboard with $n$ rows and $n$ columns. All rows of the chessboard are labelled with $1$ through $n$ from top to bottom. All columns of the chessboard are labelled with $1$ through $n$ from left to right. All rooks are labelled with $1$ through $n$ as well. At the very beginning, each row or column contains exactly one rook. However, Lewis allows a square with two or more rooks during the game.less

Now he starts to play a game named Supreme Command. He will provide several supreme commands to all rooks. All possible commands are in the following four different formats.ide

L $k$: Every rook moves k squares to the left;
R $k$: Every rook moves k squares to the right;
U $k$: Every rook moves k squares upward;
D $k$: Every rook moves k squares downward.
For a Supreme Command with given number $k$, if a rook, after moving less than $k$ squares, had arrived at a boundary (which locates in the left-most columns, right-most column, top row or bottom row) such that the rook cannot move further, it would stay there and not move outside the chessboard.this

He will also have several queries about rooks. The only two possible formats about queries are listed as follows.spa

? $k$: Ask the current position of the $k$-th rook;
!: Ask how many pairs of rooks there are currently located in the same square.
Your task in this problem is to answer these queries correctly.pwa

Inputcode

The input contains several test cases, and the first line contains a positive integer $T$ indicating the number of test cases which is up to $1000$.orm

For each test case, the first line contains two integers $n$ which is described as above, and m indicating the total number of supreme commands and queries, where $1 \le n,m \le 3 \times 10^5$.blog

Each of the following $n$ lines contains two integers $x$ and $y$, describing a rook located at the intersection of the $x$-th row and the $y$-th column, where $1 \le x,y \le n$.ci

Then the following $m$ lines describe all Supreme Commands and queries in chronological order, where all given parameters $k$ are integers ranged from $1$ to $n$.

We guarantee that the sum of $n$ and the sum of $m$ in all test cases are up to $10^6$ respectively.

Output

For each test case, output several lines to answer all queries.

For each query of the first type ("? $k$"), output a line containing two integers $x$ and $y$, which indicate the current position of the $k$-th rook is the intersection of the $x$-th row and the $y$-th column. You should output exactly one whitespace between these two numbers.

For each query of the second type ("!"), output a line containing an integer which indicates the number of pairs of rocks that are currently located in the same square.

Example

Input
1
4 9
3 4
2 1
4 2
1 3
L 2
? 1
? 2
R 1
? 1
? 3
!
U 3
!
Output
3 2
2 1
3 3
4 2
0
3

Note

The following figures illustrate the chessboard at the beginning and after each Supreme Commands in the sample case.

 

題意:

給定一個 $n \times n$ 的棋盤,記左上角的座標爲 $(1,1)$,右下角的座標爲 $(n,n)$。棋盤上有 $n$ 個編號 $1 \sim n$ 的棋子,每一行或者每一列都有且僅有一個棋子。

給出操做序列,操做有如下三種:

  1. $L(R,U,D) \; k$:全部棋子向左(右、上、下)移動 $k$ 格,棋子能夠重疊,若操做使棋子向邊界外移動則實際上中止不動。
  2. $? \; k$:查詢編號 $k$ 的棋子如今的位置。
  3. $!$:查詢現有多少對棋子在同一個格子裏。

 

題解:

這個題,因爲能夠棋子能夠重疊的緣故,不難想到行和列是相互獨立的。不妨分開來看,那麼問題就從二維的變成了兩個一維的合成。

不妨僅僅考慮列座標,也就是說,在一條橫軸上 $1 \sim n$ 這 $n$ 個座標點上,都放置了一枚棋子。

先考慮怎麼查詢第 $k$ 個棋子的位置,不妨先記錄下其初始位置 $pos_k$,又記 $[L,R]$ 是全體棋子位置的「緊實的」左右邊界。

那麼,因爲移動棋子的操做是全部棋子一塊兒移動。所以,咱們不妨把左移全部棋子操做看作把棋盤的左邊界 $L$ 往右推進,而把右移全部棋子操做看作把棋盤的右邊界 $R$ 往左推進。固然,這樣一來 $[L,R]$ 和棋子實際所處區間顯然是存在誤差的,能夠用一個變量 $\Delta$ 來記錄這個誤差。

這樣一來,因爲 $L$ 只能右移,$R$ 只能左移。也就是說,一顆棋子一旦碰到過邊界,就不會再離開邊界。所以,咱們根據初始位置 $pos_k$,以及目前全體棋子的左右邊界 $[L,R]$ 就能判斷第 $k$ 枚棋子是否處在邊界。固然,咱們知道 $[L,R]$ 一旦被往裏推進一段距離以後(即 $1 < L \le R < n$),必定範圍內的左右移動棋子是不會使得 $[L,R]$ 變小的,這個時候只須要調整一下 $\Delta$ 便可。

 

接下來,要考慮怎麼查詢有多少對棋子處在同一個格子內。這個時候不妨從新考慮二維原問題,也就是說如今有上下左右四條邊界往裏壓棋子。那麼很顯然的,棋子的重疊只可能發生在左上、右上、左下、右下四個角上,且棋子一旦重疊就不再可能分開。

因此,咱們只須要用 $cUL,cUR,cDL,cDR$ 分別維護撞到過上左角、上右角、下左角、下右角的棋子數目便可,而且控制全部棋子只會被計數一次。

這樣一來,只須要特判一下全部棋子被壓成一行或一列甚至一格的狀況,剩下來直接求 $C_{cUL}^2+C_{cUR}^2+C_{cDL}^2+C_{cDR}^2$ 便可。

 

AC代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;

ll C[maxn]; //C[n,2]
void prework() {
    for(int i=0;i<maxn;i++) C[i]=(ll)i*(i-1)/2;
}

int n,m;
void Cnt(int id);
struct Sol
{
    int pos[maxn],id[maxn];
    int l,r,d;
    void InitLR() {
        l=1, r=n, d=0;
    }
    void InitCnt() {
        Cnt(id[1]), Cnt(id[n]);
    }
    void SetPos(int p,int i) {
        pos[i]=p, id[p]=i;
    }
    void Left(int k)
    {
        while(l<r && l+d-k<1) Cnt(id[++l]);
        if(l+d-k<1) d=1-l;
        else d-=k;
    }
    void Right(int k)
    {
        while(l<r && r+d+k>n) Cnt(id[--r]);
        if(r+d+k>n) d=n-r;
        else d+=k;
    }
    int Ask(int id)
    {
        if(pos[id]<=l) return l+d;
        if(pos[id]>=r) return r+d;
        return pos[id]+d;
    }
}A,B;

int cLL,cLR,cRL,cRR;
bool vis[maxn];
void Cnt(int id)
{
    if(vis[id]) return;
    else if(A.pos[id]<=A.l && B.pos[id]<=B.l) cLL++, vis[id]=1;
    else if(A.pos[id]<=A.l && B.pos[id]>=B.r) cLR++, vis[id]=1;
    else if(A.pos[id]>=A.r && B.pos[id]<=B.l) cRL++, vis[id]=1;
    else if(A.pos[id]>=A.r && B.pos[id]>=B.r) cRR++, vis[id]=1;
}

ll Query()
{
    //printf("上左=%d 上右=%d 下左=%d 下右=%d\n",cLL,cLR,cRL,cRR);
    if(A.l==A.r && B.l==B.r) return C[cLL+cLR+cRL+cRR];
    else if(A.l==A.r) return C[cLL+cRL]+C[cLR+cRR];
    else if(B.l==B.r) return C[cLL+cLR]+C[cRL+cRR];
    else return C[cLL]+C[cLR]+C[cRL]+C[cRR];
}

int main()
{
    prework();
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        A.InitLR(); B.InitLR();
        for(int i=1,x,y;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            A.SetPos(x,i);
            B.SetPos(y,i);
        }

        cLL=cLR=cRL=cRR=0;
        memset(vis,0,(n+2)*sizeof(bool));
        A.InitCnt(); B.InitCnt();

        char op[3]; int k;
        while(m--)
        {
            scanf("%s",op);
            if(op[0]=='!') printf("%I64d\n",Query());
            else
            {
                scanf("%d",&k);
                if(op[0]=='U') A.Left(k);
                if(op[0]=='D') A.Right(k);
                if(op[0]=='L') B.Left(k);
                if(op[0]=='R') B.Right(k);
                if(op[0]=='?') printf("%d %d\n",A.Ask(k),B.Ask(k));
            }
        }
    }
}

注:有不少是參考標程的QAQ,要否則感受本身估計是寫不出來的QAQ。

相關文章
相關標籤/搜索