1. 程式人生 > >ICPC 2018 焦作 C題 Supreme Command

ICPC 2018 焦作 C題 Supreme Command

​​在這裡插入圖片描述

在這裡插入圖片描述
在這裡插入圖片描述

題意

給出一個 n × n n×n 的棋盤,保證每行每列只有一個棋子,給出多種操作,將所有棋子先上/下/左/右移動 k k

步(遇到邊界則停止移動),提問某一個棋子的現在的位置或問當前有多少對棋子是在一個格子裡的。

題解

棋盤的兩個方向可以分別考慮,我們現在只考慮水平方向
用區間 [ l , r ] [l,r]

表示對於初始棋盤來說,水平座標在 [ l , r ] [l,r] 範圍內的棋子是沒有觸碰到邊界的
比如樣例中的棋盤,向左移動2格後,只有初始座標在 [
3 , 4 ] [3,4]
的棋子是沒有碰到邊界的,但真實情況是座標集中在區間 [ 1 , 2 ] [1,2] ,所以還要記錄一個變數 t t 表示兩個座標的偏差值
為什麼這樣做?
這樣是為了詢問棋子座標的操作,當水平座標 x < l x<l 時,說明在邊界上,那麼 l t l-t 就是真實的座標,同理, x > r x>r 時為 r t r-t l < = x < = r l<=x<=r 時為 x t x-t
[ l , r ] [l,r] 的維護方法就留給讀者自己考慮(程式碼中有提示)
接下來是詢問在同一格子的對數
棋子的整體移動可以等效為棋盤邊界的反方向移動,那麼很容易觀察到會有多個棋子的格子只能在邊界的四個角上,這樣用4個變數維護4個角的棋子個數就可以了
維護方法
左上角的為 l i f t   a n d   u p lift\ and\ up ,所以變數為 l u lu , l d   r u   r d ld\ ru\ rd 也是同理
上邊界和下邊界有兩條掃描線
當區間 [ u , d ] [u,d] 更新時,看邊界經過的點的座標x與區間 [ l , r ] [l,r] 的關係

關係 操作
x < l x<l 左邊界已經掃過,說明這個點已經在角格子了,對應變數直接加1
l < x < r l<x<r 說明該點不在邊界,則 m a r k [ x ] = 1 mark[x]=1 ,標記一下,更新 [ l , r ] [l,r] 時加到對應變數裡
r < x r<x 右邊界已經掃過,說明這個點已經在角格子了,對應變數直接加1

當區間 [ l , r ] [l,r] 更新時,如果 m a r k [ ] = = 1 mark[]==1 ,說明之前已經遇到上下邊界,現在左右邊界又遇到了,那麼肯定以後都在角格子裡了,對應計數的變數加1

程式碼:

#include<bits/stdc++.h>
#define N 300010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define P 998244353
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

int dx,dy,n,m,l,r,u,d,p;
int x[N],y[N],a[N],b[N],c[N],lu,ld,ru,rd;
LL C[N];

void workL()
{
	//real [l-dy,r-dy]
	//if l-dy<1 update l, else update dy
    int t=l-dy-p;
    if (t>=1) dy+=p;else
    {
        int  pl=l;
        l-=t-1;
        if (l>r) l=r;
        dy=l-1;
        for (int i=pl+1;i<=l-(l==r);i++) lu+=a[i],ld+=b[i];
    }
}

void workR()
{
	//if r-dy>n update r, else update dy
    int t=r-dy+p-n;
    if (t>0)
    {
        int pr=r;
        r-=t;
        if (l>r) r=l;
        dy=r-n;
        for (int i=pr-1;i>=r+(l==r);i--) ru+=a[i],rd+=b[i];
    }else dy-=p;
}

void workU()
{
    int t=u-dx-p;
    if (t>=1) dx+=p;else
    {
        int pu=u;
        u-=t-1;
        if (u>d) u=d;
        dx=u-1;
        for (int i=pu+1;i<=u-(d==u);i++){
            if (c[i]>=r) ru++;else
            if (c[i]>l) a[c[i]]=1;else lu++;
        }
    }
}

void workD()
{
    int t=d-dx+p-n;
    if (t>0)
    {
        int pd=d;
        d-=t;
        if (u>d) d=u;
        dx=d-n;
        for (int i=pd-1;i>=d+(d==u);i--){
            if (c[i]>=r) rd++;else
            if (c[i]>l) b[c[i]]=1;else ld++;
        }
    }else dx-=p;
}

void cal()
{
    if (l==r && u==d) {
        printf("%lld\n",C[n]);
    }else
    if (l==r){
        printf("%lld\n",C[lu+ru]+C[ld+rd]);
    }else
    if (u==d){
        printf("%lld\n",C[lu+ld]+C[ru+rd]);
    }else
    printf("%lld\n",C[lu]+C[ru]+C[ld]+C[rd]);
}

void query()
{
    int ax=max(min(x[p],d),u),ay=max(min(y[p],r),l);
    ax-=dx; ay-=dy;
    printf("%d %d\n",ax,ay);
}

int main()
{
    for (int i=0;i<N;i++) C[i]=(LL) i*(i-1)/2;
    int T;
    sc(T);
    while(T--)
    {
        scc(n,m);
        dx=dy=0;l=u=0;r=d=n+1;
        for (int i=1;i<=n;i++) scc(x[i],y[i]),a[i]=b[i]=0,c[x[i]]=y[i];
        lu=ld=ru=rd=0;
        while(m--)
        {
            char x[4];
            scanf("%s",x);
            if (x[0]=='!') cal(); else
            {
                sc(p);
                if (x[0]=='L') workL();else
                if (x[0]=='R') workR();else
                if (x[0]=='U') workU();else
                if (x[0]=='D') workD();else
                if (x[0]=='?') query();
            }
        }
    }
}