jz集训 8.1

Day 1

0

T1 游戏

Description

Alice和Bob在玩一个游戏,游戏是在一个N*N的矩阵上进行的,每个格子上都有

一个正整数。当轮到Alice/Bob时,他/她可以选择最后一列或最后一行,并将其删除,但

必须保证选择的这一行或这一列所有数的和为偶数。如果他/她不能删除最后一行或最后一

列,那么他/她就输了。两人都用最优策略来玩游戏,Alice先手,问Alice是否可以必胜?

Input

第一行:T,表示数据组数

对于每组数据的第一行:N

接下来N行,每行N个数,描述这个矩阵

Output

如果Alice必胜输出W,否则输出L

Sample Input

2
2
2 4
6 8
3
5 4 2
1 5 9
7 3 8

Sample Output

L
W

Hint

100%数据满足

1<=N<=1000

保证每一行或每一列的和不会超过2*10^9

1<=T<=5

30%数据满足

1<=N<=5

50%数据满足

1<=N<=100

70%数据满足

1<=N<=500

Solution

博弈论。当一个状态经过操作可以转到必败状态,该状态必胜。但是注意可以转到必胜状态的不一定必败。
考虑什么时候是必胜/必败状态:当只有一行,且和为偶数时为必胜状态。当和为奇数时必败。
接下来可以写转移了:当能转到必败状态时,该状态必胜。

Code

#include <cstdio>
#include <cstring>
const int N = 1001;
int t,n;
int a[N][N];
int k[N][N];
int main(){
	scanf("%d",&t);
	while(t--){
		memset(a, 0, sizeof a);
		memset(k, 0, sizeof k);
		scanf("%d",&n);
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				scanf("%d",&a[i][j]);
				a[i][j] &= 1;
				a[i][j] = a[i-1][j]^a[i][j-1]^a[i-1][j-1]^a[i][j];
			}
		}
		if(a[1][1]){
			k[1][1]=0;
		}
		else{
			k[1][1]=1;
		}
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				if(i==1 && j==1){
					continue;
				}
				if((i>1 && (a[i][j]^a[i-1][j])==0 && k[i-1][j]==0) || (j>1 && (a[i][j]^a[i][j-1])==0 && k[i][j-1]==0) || ((j==1 || i==1) && a[i][j]==0)){
					k[i][j]=1;
				}
				else{
					k[i][j]=0;
				}
			}
		}
		if(k[n][n]){
			printf("W\n");
		}
		else{
			printf("L\n");
		}
	}
	return 0;
}

T2 六边形

Description

棋盘是由许多个六边形构成的,共有5种不同的六边形编号为1到5,棋盘的生成规

则如下:

1.从中心的一个六边形开始,逆时针向外生成一个个六边形。

2.对于刚生成的一个六边形,我们要确定它的种类,它的种类必须满足与已生成的相

邻的六边形不同。

3.如果有多个种类可以选,我们选择出现次数最少的种类。

4.情况3下还有多个种类可以选,我们选择数字编号最小的。

现在要你求第N个生成的六边形的编号?

前14个六边形生成图如下:

Input

第一行:T,表示数据组数

接下来T行,每行一个数:N,表示第N个六边形

Output

共t行,每行一个数,表示第N个数据的答案

Sample Input

2
2
2 4
6 8
3
5 4 2
1 5 9
7 3 8

Sample Output

L
W

Hint

100%数据满足

1<=N<=1000

保证每一行或每一列的和不会超过2*10^9

1<=T<=5

30%数据满足

1<=N<=5

50%数据满足

1<=N<=100

70%数据满足

1<=N<=500

Solution


我觉得以中心为1号点规律性不大,所以我将每个点的编号都-1,观察到以下规律:
1.以中心为第0圈,第 n n n圈的六边形数为 6 n 6n 6n
2.在每一圈中有6个特殊位置(即途中蓝色线所标出的),它们在内侧相邻的只有一个六边形,而非特殊位置的都有2个。
我们注意到“特殊位置”的处理十分重要。
将每一圈再单独标号,如上图7号点标为第二圈的1号,8号点是第三圈的2号…以此类推,记这个编号为 n u m i num_i numi。图中第二列是 n u m i num_i numi的示例。
将该点在第几圈内记为 c i r i cir_i ciri
n u m i num_i numi% c i r i = 0 cir_i=0 ciri=0 i i i在特殊位置。
3.有了特殊位置,我们就可以注意到非特殊的点了。
我们为它们再重新标一个号,但这个序号要排去特殊点。
例如:7号点不是特殊点,标为1号,8号点是特殊点,跳过,9号点不是特殊点,标为2号…记为 c n t i cnt_i cnti。图中第三列是 c n t i cnt_i cnti的示例。
注意到这些点与2个点相邻,具体的是上一圈 c n t = = c n t i cnt==cnt_i cnt==cnti c n t = = c n t i − 1 cnt==cnt_i-1 cnt==cnti1的两个点。
有了这些我们就掌握了相邻的规律,可以递推了。
注意每一圈的第一个除了与内侧相邻以外没有相邻的点。最后一个除了与内侧相邻以外还有与这一圈的第一个点相邻。

Code

#include <cstdio>
#include <iostream>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 10100;
int t,n;
int ans[N]={1,2,3,4,5,2,3};
struct data{
	int col,t;
	bool operator < (const data & p) const {
		return this->t==p.t ? this->col < p.col : this->t < p.t;
	}
}datas[10];
void init(){
	for(int i=1; i<=5; i++){
		datas[i].col=i;
	}
	datas[1].t=1;
	datas[2].t=2;
	datas[3].t=2;
	datas[4].t=1;
	datas[5].t=1;
	int  cir=2,num=0,cnt=0,tot=6;//tot表示上一圈最后一个的位置 
	bool blk[6];
	for(int i=7; i<=10005; i++){
		memset(blk, 0, sizeof blk); //清空数组 blk表示与该点冲突 
		sort(datas+1,datas+6);  //按照题目要求sort 
		num=i-tot;  //num 表示是该圈第num个 
		
		/*以下是求与内圈冲突的*/ 
		if(num%cir==0){ //如果在6个特殊位置 
			int id=num/cir; //判断在第几个特殊位置 
			blk[ans[tot-(cir-1)*6+(cir-1)*id]]=1;
		}
		else{
			cnt++;
			if(cnt==1){	
				blk[ans[tot]]=1;
				blk[ans[tot-(cir-1)*6+1]]=1;
			}
			else{
				blk[ans[tot-(cir-1)*6+cnt]]=1;
				blk[ans[tot-(cir-1)*6+cnt-1]]=1;
			}
		}
		/**********************/
		
		if(i==tot+6*cir){
			blk[ans[tot+1]]=1;
			tot=tot+6*cir;
			cir++;
			cnt=0;
		}
		
		else if (i!=tot+1){
			blk[ans[i-1]]=1;
		}
		for(int j=1; j<=5; j++){
			int col=datas[j].col;
			if(!blk[col]){
				datas[j].t++;
				ans[i]=col;
				break;
			}
		}
		continue;
	}
}
int main(){
	scanf("%d",&t);
	init();
	while(t--){
		scanf("%d",&n);
		printf("%d\n",ans[n-1]);
	}
	return 0;
}

T3 数列

Description

给你一个长度为N的正整数序列,如果一个连续的子序列,子序列的和能够被K整

除,那么就视此子序列合法,求原序列包括多少个合法的连续子序列?

对于一个长度为8的序列,K=4的情况:2, 1, 2, 1, 1, 2, 1, 2 。它的答案为6,子序列

是位置1->位置8,2->4,2->7,3->5,4->6,5->7。

Input

第一行:T,表示数据组数

对于每组数据:

第一行:2个数,K,N

第二行:N个数,表示这个序列

Output

共T行,每行一个数表示答案

Sample Input

2
7 3
1 2 3
4 8
2 1 2 1 1 2 1 2

Sample Output

0
6

Solution

用前缀和记录从第一个开始的序列%k的余数。
当sum[i]=sum[j]时,序列ai+1-aj就是整除k的。
记录有多少个相同的,每一种相同的数就加上 C 2 c n t C^{cnt}_{2} C2cnt

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define int long long
using namespace std;
const int N = 50001;
int t,n,k,ans;
int s[N];
int work(int x,int y){
	int sum=1;
	int i=x,j=1;
	while(j<=y){
		sum=sum*i/j;
		j++;
		i--;
	}
	return sum;
}
signed main(){
	scanf("%lld",&t);
	while(t--){
		s[0]=0;
		ans=0;
		scanf("%lld%lld",&k,&n);
		for(int i=1; i<=n; i++){
			int x;
			scanf("%lld",&x);
			s[i]=(s[i-1]+x)%k;
		}
		sort(s+1,s+1+n);
		int cnt=1;
		for(int i=1; i<=n; i++){
			if(s[i]==s[i-1]){
				cnt++;
			}
			else{
				ans+=work(cnt,2);
				cnt=1;
			}
		}
		ans+=work(cnt,2);
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值