zoukankan      html  css  js  c++  java
  • 20180520模拟赛T3——chess

    【问题描述】

    小美很喜欢下象棋。

    而且她特别喜欢象棋中的马。

    她觉得马的跳跃方式很独特。(以日字格的方式跳跃)

    小芳给了小美一张很大的棋盘,这个棋盘是一个无穷的笛卡尔坐标。

    一开始(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\%)的数据(Kle 500)

    对于(100\%)的数据(0le K le 10^{18}, 0le nle 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当场也没打出来的题。

  • 相关阅读:
    mysql类似oracle rownum写法
    mysql派生查询必须有别名问题记录
    MySQL逻辑架构简介
    JSON转成List结构数据
    MySQL锁表查询SQL
    Http请求传json数据中文乱码问题
    异步调用导致的不同步问题
    oracle导入Excel表文本数据
    JSP页面的注释细节
    Oracle cursor学习笔记
  • 原文地址:https://www.cnblogs.com/pfypfy/p/9094182.html
Copyright © 2011-2022 走看看