1. 程式人生 > >20180520模擬賽T3——chess

20180520模擬賽T3——chess

驗證 problems 由於 結果 是不是 IE 什麽 lin 不能

【問題描述】

小美很喜歡下象棋。

而且她特別喜歡象棋中的馬。

她覺得馬的跳躍方式很獨特。(以日字格的方式跳躍)

小芳給了小美一張很大的棋盤,這個棋盤是一個無窮的笛卡爾坐標。

一開始\(time=0\)的時候,馬在原點。每個時刻馬都跳一步。

可是這個坐標圖有點殘缺,有幾個點是不能跳到的。

然後小美很好奇在\(time=[0,K]\)中,馬能跳到多少個不同的格子。

【輸入格式】

從文件chess.in中讀入數據。

第一行兩個數K,n表示時間上限和殘缺的點的數量。

接下來n行,每行一個坐標 xi,yi 表示一個殘缺點的坐標。

【輸出格式】

輸出到文件chess.out中。

第一行輸出一個數字表示答案。由於這個數字會很大,你需要輸出他模 1000000007 。

【樣例輸入】

1 0

【樣例輸出】

9

【樣例輸入】

2 7
-1 2
1 2
2 1
2 -1
1 -2
-1 -2
-2 -1

【樣例輸出】

9

【數據規模】

對於\(30\%\)的數據\(K\le 500\)

對於\(100\%\)的數據\(0\le K \le 10^{18}, 0\le n\le 440, |xi|\le10, |yi|\le 10\)

【來源】

CF 57E

題解

一道神奇的亂搞題。

首先,對於一個oier,最基本的技能是暴力,於是我們先用bfs打一個暴力(代碼下面會有)。

於是就拿到了37分的好成績。

技術分享圖片

但是除了大暴力,我們似乎並沒有什麽路可走——因為情況非常復雜。

那麽這題為什麽這麽復雜?

因為有這句話:可是這個坐標圖有點殘缺,有幾個點是不能跳到的。

於是我們想到簡化:那這句話不在會怎麽樣?

我們發現一時找不出規律,但我們可以打張表。

#include <cstdio>
#include <queue>

using namespace std;

int mp[2003][2003];
int ans[6000];//ans[i]:第i步所到新到達的地方

int dirx[] = {1, 1, -1, -1, 2, 2, -2, -2};
int diry[] = {2, -2, 2, -2, 1, -1, 1, -1};

struct sxd
{
    int
x, y; }; queue<sxd> Q; int n; long long k; long long cnt; inline void bfs(int x, int y) { mp[x][y] = 1; Q.push((sxd){x, y}); while(!Q.empty()) { sxd tmp = Q.front(); Q.pop(); ans[mp[tmp.x][tmp.y]]++; if(mp[tmp.x][tmp.y] > 70) break; for(int i = 0; i < 8; ++i) { int tx = tmp.x + dirx[i]; int ty = tmp.y + diry[i]; if(mp[tx][ty]) continue; mp[tx][ty] = mp[tmp.x][tmp.y] + 1; Q.push((sxd){tx, ty}); } } } int main() { bfs(1001, 1001); for(int i = 1; i <= 60; ++i) printf("%d,", ans[i]); return 0; }

運行結果:

1,8,32,68,96,120,148,176,204,232,260,288,316,344,372,400,428,456,484,512,540,568,596,624,652,680,708,736,764,792,820,848,876,904,932,960,988,1016,1044,1072,1100,1128,1156,1184,1212,1240,1268,1296,1324,1352,1380,1408,1436,1464,1492,1520,1548,1576,1604,1632,

發現了什麽?(並沒有發現什麽)到了後面項,該數列竟趨向於一個等差數列!

printf("%d,", ans[i]);改成printf("%d,", ans[i+1]-ans[i]);會更清晰:

7,24,36,28,24,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,

然後我們就可以大膽猜想了……

我們發現數據規模中\(|xi|, |yi|\)的值很小,是不是這樣就不怎麽會影響等差數列的波動幅度?

於是我們需要驗證一下

//前面只是把if(mp[tmp.x][tmp.y] > 70)改成了if(mp[tmp.x][tmp.y] > 200)

int main()
{
    bfs(1001, 1001);
    cin >> n;
    for(int i = 1, x, y; i <= n; ++i)
    {
        cin >> x >> y;
        x += 1001;
        y += 1001;
        mp[x][y] = 1;//這樣做還是挺妙的,把殘缺坐標直接看成已訪問
    }
    for(int i = 1; i <= 150; ++i)//輸出多一些,讓結論更可信
        printf("%d,", ans[i+1] - ans[i]);
    return 0;
}

隨便造組數據驗證一下:

8
2 3
1 2
2 1
5 6
-1 -2
-2 -1
-3 -4
-3 -2

輸出

7,24,36,28,24,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,

經過多次驗證,我們就可以很方便地想到正解:先暴力搜索幾步(似乎都是選擇500步的),然後用等差數列直接求出答案。

於是代碼如下

//前面的bfs都差不多,就在寫一遍了。

signed main()
{
    fin >> k >> n;//由於當時是模擬賽所以用了文件流
    for(int i = 1, x, y; i <= n; ++i)
    {
        fin >> x >> y;
        x += 1001;
        y += 1001;
        mp[x][y] = 1;
    }
    bfs(1001, 1001);
    if(k < 500)
    {
        for(int i = 1; i <= k+1; ++i)
            cnt = (cnt+ans[i])%mod;
        fout << cnt << endl;
    }
    else
    {
        long long inc = ans[500] - ans[499];//等差數列的公差
        k -= 498;
        k %= mod;
        for(int i = 0; i <= 499; ++i)
            cnt = (cnt+ans[i])%mod;
        fout << (((k*ans[500]%mod+cnt)%mod+(k*(k-1)>>1)%mod*inc%mod)%mod+mod)%mod << endl;
    }
    return 0;
}

最後,恭喜你打出了一道tourist當場也沒打出來的題。

20180520模擬賽T3——chess