1. 程式人生 > >Gym 102028C - Supreme Command - [思維題][2018-2019 ACM-ICPC Asia Jiaozuo Regional Contest Problem C]

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

題目連結:

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.

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.

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.

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

? $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.

Input

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$.

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$.

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$.

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$ 往左推。這樣一來,根據初始位置 $pos[k]$ 和 $[L,R]$ 就能判斷第 $k$ 枚棋子有沒有撞到過邊界。

然後,我們再用一個變數 $\Delta$ 來記錄下這個 $[L,R]$ 和實際棋子所在區間的位移差即可。

接下來,要考慮怎麼查詢有多少對棋子處在同一個格子內。顯然,棋子的重疊只可能發生在左右兩個端點,而且一旦棋子重疊了就再也不可能分開。

因此,對於壓成一行的棋盤,一顆棋子一旦貼到過一次邊界,它就有了重疊的貢獻。我們只需要開四個 $cnt[1 \sim 4]$ 分別維護貼到過左上邊、左下邊、右上邊、右下邊的棋子數目即可。這樣一來,只需要特判一下所有棋子被壓成一行或一列或一格的情況,其餘的直接求 $C(cnt[1],2)+C(cnt[2],2)+C(cnt[3],2)+C(cnt[4],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()
{
    C[0]=C[1]=0;
    for(int i=2;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;
    #define t(x) (x+d)
    void initLR() {
        l=1, r=n, d=0;
    }
    void initCnt() {
        Cnt(id[1]), Cnt(id[n]);
    }
    void Move(int k)
    {
        if(k>0) //右移
        {
            if(t(r)+k>n)
            {
                while(t(r)+k>n && l<r) Cnt(id[--r]);
                d=n-r;
            }
            else d+=k;
        }
        if(k<0) //左移
        {
            if(t(l)+k<1)
            {
                while(t(l)+k<1 && l<r) Cnt(id[++l]);
                d=1-l;
            }
            else d+=k;
        }
    }
    int Ask(int id)
    {
        if(pos[id]<l) return t(l);
        if(pos[id]>r) return t(r);
        return t(pos[id]);
    }
}row,col;
int cnt_lu,cnt_ld,cnt_ru,cnt_rd;
void Cnt(int id)
{
    if(row.pos[id]<=row.l && col.pos[id]<=col.l) cnt_lu++;
    if(row.pos[id]<=row.l && col.pos[id]>=col.r) cnt_ld++;
    if(row.pos[id]>=row.r && col.pos[id]<=col.l) cnt_ru++;
    if(row.pos[id]>=row.r && col.pos[id]>=col.r) cnt_rd++;
}
ll Query()
{
    if(row.l==row.r && col.l==col.r) return C[n];
    if(row.l==row.r) return C[cnt_lu]+C[cnt_ld];
    if(col.l==col.r) return C[cnt_lu]+C[cnt_ru];
    return C[cnt_lu]+C[cnt_ld]+C[cnt_ru]+C[cnt_rd];
}

int main()
{
    prework();
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            row.pos[i]=y, row.id[y]=i;
            col.pos[i]=x, col.id[x]=i;
        }

        cnt_lu=cnt_ld=cnt_ru=cnt_rd=0;
        row.initLR(), col.initLR();
        row.initCnt(), col.initCnt();

        char op[2]; int k;
        while(m--)
        {
            scanf("%s",op);
            switch(op[0])
            {
            case 'L':
                scanf("%d",&k);
                row.Move(-k);
                break;
            case 'R':
                scanf("%d",&k);
                row.Move(k);
                break;
            case 'U':
                scanf("%d",&k);
                col.Move(-k);
                break;
            case 'D':
                scanf("%d",&k);
                col.Move(k);
                break;
            case '?':
                scanf("%d",&k);
                printf("%d %d\n",col.Ask(k),row.Ask(k));
                break;
            case '!':
                printf("%I64d\n",Query());
                break;
            default:break;
            }
        }
    }
}

講真,感覺這題好難QAQ,上面的程式碼還是照搬標程的QAQ