round 1

1 篇文章 0 订阅
1 篇文章 0 订阅

冲刺NOIP2017模拟赛R1

 

五子棋

定向越野

孤立元素

源文件名

five

road

lonely

读入文件

five.in

road.in

lonely.in

输出文件

five.out

road.out

lonely.out

时间限制

2s

2s

2s

空间限制

256MB

256MB

256MB

最终评测时不开启任何优化开关

 

五子棋

风和日丽的一天,漳平一中的红太阳Mektpoy邀请蒟蒻CRZ一起playing五子棋game,游戏过程由于CRZMektpoy学长的无限崇拜而变得凌乱起来??你可以理解成,由于对帅副的崇高信仰,两人已经下到一个忘我的局面了(暂时忽视了所有五子棋规则)。现在,局面又轮到该CRZ下了,可怜的CRZ想知道,他下完这一步后能否取胜,如果能,请顺便告诉他一共有多少种不同的方案使得他能取得胜利。

 

[输入格式]

第一行读入一个正整数T,表示共有T组数据

对于每组数据,第一行读入两个正整数n,m

接下来的n行每行m个字符,每个字符的含义可以参考文末的游戏规则

 

[输出格式]

对于每组数据

第一行输出一个字符串YES’如果CRZ下完一步以后能取胜,否则输出‘NO

如果第一问的答案是YES’再输出一行一个数字,表示CRZ取胜的方案数

 

[样例输入1]

1

10 10

XX.XX.....
.....OOOO.
..........
..........
..........
..........
..........
..........
..........
..........

 

[样例输出1]

YES

1

 

[样例输入2]

1

10 10

XXOXX.....
OO.O......
..........
..........
..........
..........
..........
..........
..........
..........

 

[样例输出2]

NO

了防止由于题目理解失误而导致不该出现的情况,这里贴出Mektpoy-CRZ版五子棋游戏规则:游戏过程中,Mektpoy走的每一步都用‘O’表示,CRZ走的每一步都用“X”表示,而如果一个格子是‘.’则表示还没有人在这里落过子。胜利局面的判定方式同正常的五子棋游戏(任意一方垂直、平行、斜着五子连珠均可)。输入会给出一个局面,不保证在正经五子棋规则下能走出这个局面,所以不必在意这些。你需要统计的方案数等价于CRZ放完一个‘X’以后能出现五子连珠的方案数。特别的,谁先达成五子连珠谁就算取胜,并且无论如何,CRZ一定要落下一枚棋子

 

[数据范围]

对于10%的数据,保证1 <= n,m <= 3

对于另20%的数据 ,保证1 <= n,m <= 10

对于所有数据,1 <= T <= 5,保证1 <= n,m <= 1000

保证总有地方可以让CRZ放一枚棋子

保证不会出现给定的局面中双方都已经达成五子连珠的情况

 


一个略大的模拟。

分3种情况讨论。

如果已经输了,就输出NO。

如果已经赢了,怎么下都能赢,统计能下的位置的个数并输出。

还没赢的话,就判断这个位置下下去能不能赢并计数就行了。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1000;
int T,N,M;
int ans,flag,toto;
bool flagt,flago;
char s[maxn+10];
int a[maxn+10][maxn+10];
inline void Work(){
	if(flag == 0) ans++;
	if(flag == 1){
		printf("YES\n");
		printf("%d\n",toto);
		flagt = true;
	}
	if(flag == 2){
		printf("NO\n");
		flagt = true;
	}	
	return;
}
int main(){
	scanf("%d",&T);
	while(T--){
		toto = 0;
		memset(a,-1,sizeof(a));
		scanf("%d %d",&N,&M);
		for(int i = 1; i <=N; i++){
			scanf("%s",s+1);
			for(int j = 1; j <= M; j++){
				if(s[j] == 'X') a[i][j] = 1;
				if(s[j] == 'O') a[i][j] = 2;
				if(s[j] == '.') a[i][j] = 0,toto++;
			}
		}
		flagt = false;
		ans = 0;
		for(int i = 1; i <= N; i++){
			for(int j = 1; j <= M; j++){
				if(flagt) break;
				int tot = 0;
				flag = -1;
				flago = false;
				if(a[i][j] == 0) a[i][j] = 1,flago = true;
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i+k][j]) tot++;
					else break;
				}
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i-k][j]) tot++;
					else break;
				}
				if(tot >= 4){
					if(flago) a[i][j] = 0;
					flag = a[i][j];
					Work();
					continue;
				}
				tot = 0;
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i][j+k]) tot++;
					else break;
				}
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i][j-k]) tot++;
					else break;
				}
				if(tot >= 4){
					if(flago) a[i][j] = 0;
					flag = a[i][j];
					Work();
					continue;
				}
				tot = 0;
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i+k][j+k]) tot++;
					else break;
				}
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i-k][j-k]) tot++;
					else break;
				}
				if(tot >=4){
					if(flago) a[i][j] = 0;
					flag = a[i][j];
					Work();
					continue;
				}
				tot = 0;
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i+k][j-k]) tot++;
					else break;
				}
				for(int k = 1; k <= 5; k++){
					if(a[i][j] == a[i-k][j+k]) tot++;
					else break;
				}
				if(tot >= 4){
					if(flago) a[i][j] = 0;
					flag = a[i][j];
					Work();
					continue;
				}
				if(flago) a[i][j] = 0;
			}
			if(flagt) break;
		}
		if(!flagt){
			if(ans != 0){
				printf("YES\n");
				printf("%d\n",ans);
			}
			else printf("NO\n");
		}
	}
	return 0;
} 

要注意的是读入单个字符是可以读回车符的。

读字符串可以使用

scanf("%s",s+1);

让字符串从s[1]的位置开始读入字符。


定向越野

风和日丽的一天,漳平一中的红太阳CRZ邀请蒟蒻Mektpoy一起playing定向越野game,游戏过程由于MektpoyCRZ的无限崇拜而变得凌乱起来??你可以理解成,由于对钊爷的崇高信仰,两人已经跑到一个忘我的局面了(暂时忽视了所有定向越野规则)。现在,钊爷对Mektpoy进行了Q次定向轰炸,可怜的Mektpoy想知道,他在每一次轰炸中能否生存下来,如果能,请顺便告诉他最短需要在多长时间到达目的地。(你可以认为Mektpoy跑的很慢,每单位时间只能跑1单位长度)

 

[输入格式]

第一行读入三个正整数n,m,Q,表示当前定向越野的地图抽象出来的点数和边数以及钊爷进行的轰炸数。

接下来的m行每行三个正整数x,y,z,表示地图上x点到y点有一条单向边,长度为z

接下来的Q行每行两个正整数s,t,表示当前钊爷需要让Mektpoy进行以s为起点,t为终点的定向越野。

 

[输出格式]

对于钊爷的每次轰炸,如果Mektpoy能成功完成,则输出一个正整数表示最短多长时间能够完成。如果可怜的Mektpoy无论如何都无法完成,请输出-1

 

[样例输入1]

3 4 5

1 3 7

2 3 4

3 2 4

1 2 1

1 2

1 3

2 3

3 2

3 1

[样例输出1]

1

5

4

4

-1

 

[数据范围]

对于20%的数据,2 <= n <= 10

对于60%的数据,2 <= n <= 100

对于100%的数据,2 <= n <= 500

对于所有数据,保证每条边权不超过10^6,询问个数不超过10^5

裸floyed

#include<cstdio>
#include<iostream>
using namespace std;
const int maxm = 1E9;
const int maxn = 510;
int N,M,Q;
int f[maxn][maxn];
int main(){
	scanf("%d %d %d",&N,&M,&Q);
	for(int i = 1; i <= N; i++){
		for(int j = 1; j <= N; j++){
			f[i][j] = maxm; 
		}
	}
	for(int i = 1; i <= N; i++) f[i][i] = 0;
	for(int i = 1; i <= M; i++){
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		f[x][y] = min(f[x][y],z);
	}
	for(int k = 1; k <= N; k++){
		for(int i = 1; i <= N; i++){
			for(int j = 1; j <= N; j++){
				f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
	for(int i = 1; i <= Q; i++){
		int x,y;
		scanf("%d %d",&x,&y);
		if(f[x][y] == maxm) printf("-1\n");
		else printf("%d\n",f[x][y]);
	}
	return 0;
}

孤立元素

NOI原地爆炸的宋伊雪同学无奈开始了高三总复习。风和日丽的一天,班主任数学老师正在给全班同学讲评有关于必修一集合内容的作业。于是宋伊雪碰到了这个题:

S={1,2,3,4,56},A是S的一个子集,当x∈A时,若x+1∉A,且x-1∉A,则称x是A的一个孤立元素.那么S的任意子集中,不含孤立元素的子集共有______个.

老师使用的方法是“暴力枚举”,把所有每一个合法集合都列出来然后得到答案。而宋伊雪同学貌似除了枚举也想不出更好的办法??

不过好在宋伊雪是个OIer,课后他可以使用强大的工具——计算机来帮他解决这个问题,现在,他把这个增强过的问题留给了你,你能解决吗?

 

[输入格式]

第一行一个正整数T

接下来T行每行一个正整数n,表示集合S的大小

 

[输出格式]

输出T行,每行一个整数ans,表示对于给定的n,合法方案数对1000000007取模的结果

 

 

 

[样例输入1]

1

6

 

[样例输出1]

21

 

[样例解释]

 

以下是所有合法方案

{1,2}{2,3}{3,4}{4,5}{5,6}

{1,2,3}{2,3,4}{3,4,5}{4,5,6}

{1,2,3,4}{1,2,4,5}{1,2,5,6}{2,3,4,5}{2,3,5,6}{3,4,5,6}

{1,2,3,4,5}{1,2,3,5,6}{1,2,4,5,6}{2,3,4,5,6}

{1,2,3,4,5,6}

 

注意,对于每个给定的n表示求S = {1,2,3,...,n}时的答案

 

[数据范围]

对于10%的数据,保证 1 <= n <= 10

对于另10%的数据,保证 1 <= n <= 20

对于另20%的数据,保证 1 <= n <= 1000

对于另20%的数据,保证 1 <= n <= 1000000

对于所有数据,保证1 <= T <= 1000001 <= n <= 1000000000

经过一番推敲后,用

f[n][0]表示不选n位置的答案数。

f[n][1]表示n位置选且为“孤立元素”,其余位置合法的答案数。

f[n][2]表示n位置选的合法答案数。

于是可以得到:

f[n][0] = f[n-1][0] + f[n-1][2]

f[n][1] = f[n-1][0] 

f[n][2] = f[n-1][1] + f[n-1][2]

答案应该在f[n+1][0]处或f[n][0] + f[n][2]

采用滚动数组节省空间。

然后用矩阵乘法快速幂就可以在规定时间内解决了。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const LL mo = 1000000007;
const int G[3][3]={{1,0,1},{1,0,0},{0,1,1}};
struct Rec{
	int a[3][3]; Rec(){
		memset(a,0,sizeof(a));
	} 
	Rec operator * (const Rec &b){
		Rec c; LL tmp;
		for(int i = 0; i < 3; i++){
			for(int j = 0; j < 3; j++){
				for(int k = 0; k < 3; k++){
					tmp = 1LL * a[i][k] * b.a[k][j] % mo;
					c.a[i][j] += tmp;
					if(c.a[i][j] > mo) c.a[i][j] -= mo;
				}
			}
		}
		return c;
	}
}; 
inline Rec ksm(int b){
	Rec ret,x; memcpy(x.a,G,sizeof(G));
	ret.a[0][0] = 1;
	ret.a[1][0] = 1;
	ret.a[2][0] = 0;
	while(b){
		if(b & 1) ret = x * ret;
		x = x * x;
		b >>= 1;
	}
/*	while(b){
		ret = x * ret;
		b--;
	}*/
	return ret;
}
int T;
LL n;
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%lld",&n);
		Rec now = ksm(n);
		LL ans = (now.a[0][0]) % mo;
		printf("%lld\n",ans);
	}
	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值