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==cnti−1的两个点。
有了这些我们就掌握了相邻的规律,可以递推了。
注意每一圈的第一个除了与内侧相邻以外没有相邻的点。最后一个除了与内侧相邻以外还有与这一圈的第一个点相邻。
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;
}