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

扫描二维码关注公众号,回复: 4445668 查看本文章

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

猜你喜欢

转载自www.cnblogs.com/dilthey/p/10040825.html