BestCoder Round #90
原创
©著作权归作者所有:来自51CTO博客作者Somethingwill的原创作品,请联系作者获取转载授权,否则将追究法律责任
题目链接:点击打开链接
Kblack loves flag
问题描述
kblack喜欢旗帜(flag),他的口袋里有无穷无尽的旗帜。
某天,kblack得到了一个n*mn∗m的方格棋盘,他决定把kk面旗帜插到棋盘上。
每面旗帜的位置都由一个整数对\left(x,y \right)(x,y)来描述,表示该旗帜被插在了第xx行第yy列。
插完旗帜后,kblack突然对那些没有插过旗帜的行和列很不满,于是他想知道,有多少行、列上所有格子都没有被插过旗帜。
kblack还要把妹,于是就把这个问题丢给了你,请你帮他解决。
输入描述
由于本题输入数据较大,所以采取在程序内生成数据的方式。
随机数产生器中有个内部变量xx初始时为seedseed,seedseed是我们提供的随机种子。每次请求生成一个\left[l,r \right][l,r]内的随机数时,它会将xx变为\left(50268147x+6082187\right)\ mod\ 100000007(50268147x+6082187) mod 100000007,然后返回x\ mod\ \left(r-l+1 \right)+lx mod (r−l+1)+l。
输入包含多组数据。第一行有一个整数TT,表示测试数据的组数,对于每组数据:
输入一行3个整数nn,mm,kk,seedseed分别表示棋盘的行数、列数、棋盘上旗帜的面数、随机种子。
接下来,你需要按顺序生成k面旗帜的位置信息。
对于每面旗帜,依次生成一个\left[1,n \right][1,n]内的随机数和一个\left[1,m \right][1,m]内的随机数,分别表示xx,yy。
如果你无法理解数据生成的过程,你可以复制以下代码并调用Init函数来生成数据(限C++选手)。
`
const int _K=50268147,_B=6082187,_P=100000007;
int _X;
inline int get_rand(int _l,int _r){
_X=((long long)_K*_X+_B)%_P;
return _X%(_r-_l+1)+_l;
}
int n,m,k,seed;
int x[1000006],y[1000006];
void Init(){
scanf("%d%d%d%d",&n,&m,&k,&seed);
_X=seed;
for (int i=1;i<=k;++i)
x[i]=get_rand(1,n),
y[i]=get_rand(1,m);
}
`
\left(1\leq T\leq 7 \right)(1≤T≤7),\left(1\leq n,m\leq 1000000 \right)(1≤n,m≤1000000),\left(0\leq k\leq 1000000 \right)(0≤k≤1000000),\left(0\leq seed<100000007 \right)(0≤seed<100000007)
输出描述
对于每组测试数据输出一行2个整数,分别表示没有被插过旗帜的行、列数目。
输入样例
2
4 2 3 233
3 4 4 2333
输出样例
2 1
1 0
Hint
第1组数据的旗帜的位置依次为:\left(4,2\right)(4,2),\left(1,2\right)(1,2),\left(1,2\right)(1,2)
第2组数据的旗帜的位置依次为:\left(2,1 \right)(2,1),\left(2,3\right)(2,3),\left(3,4\right)(3,4),\left(3,2\right)(3,2)
思路:刚开始用 set 去维护 TLE
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
using namespace std;
const int _K=50268147,_B=6082187,_P=100000007;
int n,m,k,seed;
int _X;
int get_rand(int _l,int _r)
{
_X=((long long)_K*_X+_B)%_P;
return _X%(_r-_l+1)+_l;
}
int x[1000006],y[1000006];
bool vx[1000006],vy[1000006];
void Init()
{
scanf("%d%d%d%d",&n,&m,&k,&seed);
_X=seed;
int cnt1=0,cnt2=0;
memset(vx,0,sizeof(vx));
memset(vy,0,sizeof(vy));
for(int i=1;i<=k;++i)
{
x[i]=get_rand(1,n);
if(!vx[x[i]])
{
cnt1++;
vx[x[i]]=1;
}
y[i]=get_rand(1,m);
if(!vy[y[i]])
{
cnt2++;
vy[y[i]]=1;
}
}
printf("%d %d\n",n-cnt1,m-cnt2);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
Init();
}
return 0;
}
/*
1000000 1000000 1000000 10000007
*/
题目链接:
点击打开链接
dingyeye loves stone
问题描述
dingyeye喜欢和你玩石子游戏。
dingyeye有一棵nn个节点的有根树,节点编号为00到n-1n−1,根为00号节点。游戏开始时,第ii个节点上有a[i]a[i]个石子。两位玩家轮流操作,每次操作玩家可以选择一个节点,并将该节点上的一些石子(个数不能为00)移动到它的父亲节点上去。如果轮到某位玩家时,该玩家没有任何合法的操作可以执行,则判负。
你在游戏中执先手,你想知道当前局面你能否必胜。
输入描述
本题有多组数据,第一行为一个非负整数TT,表示数据组数。
对于每组数据,第一行一个整数nn,表示节点数目。
接下来一行为n-1n−1个整数fa[1]\cdots fa[n-1]fa[1]⋯fa[n−1],分别描述了除根节点外每个点的父亲。方便起见,保证$0\leq fa[i]\< i$。
接下来一行为nn个非负整数a[0]\cdots a[n-1]a[0]⋯a[n−1],分别描述了每个点初始的石子数。保证0\leq a[i]<1342177280≤a[i]<134217728。
1\leq T\leq 1001≤T≤100,1\leq n\leq 1000001≤n≤100000。
保证n>100n>100的测试点数目不超过77个。
输出描述
对于每组数据,输出一行,若先手必胜则输出"win",否则输出"lose"(不含引号)。
输入样例
2
2
0
1000 1
4
0 1 0
2 3 3 3
输出样例
win
lose
思路:设根节点的深度为00,将所有深度为奇数的节点的石子数目 xor 起来,则先手必胜当且仅当这个 xor 和不为0。 证明同阶梯博弈。对于偶深度的点上的石子,若对手移动它们,则可模仿操作;对于奇深度上的石子,移动一次即进入偶深度的点。 时间复杂度 O(n)。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
int fa[100010];
int depth[100010];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(fa,0,sizeof(fa));
scanf("%d",&n);
for(int i=1;i<n;i++) // 0是根节点,不记录
{
scanf("%d",&fa[i]);
depth[i]=depth[fa[i]]+1;
}
int ans=0;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
if(depth[i]&1)
ans=ans^x;
}
if(ans)
puts("win");
else
puts("lose");
}
return 0;
}