题目: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20980
做题转到vj去咯,各路护国神牛刷题的地方,怪不得经常能看到hdu上边有个vjudge的家伙快速切各种神题((⊙﹏⊙))
//更新内容
//更新内容
//更新内容
//更新内容
刚刚学了SG函数。。。今天总算膜索丸了。
总之,还得学习一个。
一个游戏可以抽象地用一个有向无环图来表示,这个图中每个点都对应这一个状态,每条有向边代表从一个状态到另一个状态的合法操作;
我们可以想象一个硬币最初放在某个点上,然后两个玩家轮流将其从当前的点移动到它的后继点。当硬币移动到汇点时游戏结束,最后一个需要操作的玩家就是胜者;
P和N状态归纳性地描述如下:
一个点v是P状态当且仅当它的所有后继都为N状态
一个点v是N状态当且仅当它的一些后继是P状态
如果G1和G2 是公平游戏,那么他们的和G1 + G2是另一个公平游戏,玩法如下:每个回合,一个玩家选择G1, G2 中的一个,当 G1 和 G2都不能操作时游戏结束。
形式上,如果 G1 = (V1, E1) 和 G2 = (V2, E2)是游戏图,那么他们的和 Gsum = (Vsum, Esum) 规定为:
Vsum = V1 × V2,
Esum = {(v1v2, w1v2) | (v1, w1) ∈ E1} ∪ {(v1v2, v1w2) | (v2, w2) ∈ E2}.
现在,假定我们给出两个游戏G1 和 G2。如果我们只知道单个游戏的P状态和N状态我们能够正确地玩好游戏和G1 + G2吗?答案是否定的。不难看出两个P状态的和总是P状态,P状态和N状态的和总是N状态。但是两个N状态的和既可能是P状态也可能是N状态。因此,只知道单个游戏的P状态和N状态是不够的。
为了正确地玩好游戏和我们需要推广P状态和N状态,它就是Sprague-Grudy函数(或者简称为Grundy函数)。
Sprague-Grundy 函数先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(x)=mex{ g(y) | y是x的后继 },这里的g(x)即sg[x]现在,给定一个游戏图G=(V,E)从G的汇点开始归纳,可知它的Grundy值为0。
Sprague-Grundy函数满足两个重要性质:
点v是一个P状态当且仅当g(v)=0
如果G = G1 + G2 且 v = v1v2 是G的一个状态,那么g(v) 为g(v1) 和 g(v2) 在二进制下的异或:
g(v) = g(v1) ⊕ g(v2).
运算⊕也称作nim和。举个例子,3 ⊕ 5 = 011 ⊕ 101 = 110 = 6
我们可以通过获知单个游戏的Grundy函数来正确地玩好任意数目游戏和。
策略如下:如果轮到我们且游戏的Grundy值给出了一个非0的nim和,那么必然在游戏的某个组分中存在一个操作使得nim和变为0。我们应该执行这个操作,那么接着我们的对手就被迫再次使得nim和非0。最终,我们将成为在最后一个游戏执行最后一个操作的人,最后将nim和变为0。
那么代码如下:
#include
#include
#include
using namespace std ;
const int maxn = 1010 ;
int pos[maxn];
int main()
{
memset(pos,0,sizeof(pos));
pos[1] = 1;
pos[2] = 1;
for(int i = 3; i < maxn; i++)
{
if(!pos[i])
{
for(int j = 0;(1<1000;j++)
pos[i+(1<1 ;
}
}
int n;
while(~scanf("%d" ,&n))
{
if(pos[n]) printf("Kiki\n") ;
else printf("Cici\n") ;
}
}
//更新内容
//更新内容
//更新内容
//更新内容
题目依然中文,我就不说题意了。
分析:
开始看到二次幂就逼脸一懵,不知道怎么写啊(就是蠢蛋不知道怎么变模板)。就决定暴力膜一发,找找规律。
下边是NP图;
1 2 3 4 5 6 7 8 9 10 11 12
N N P N N P N N P N N P
逢三就p。。。
当时不甚了解就水了一发n%3,居然过了。。
继续分析:
任何一个数%3 无非0 1 2,除了三的倍数,也就是说任何一个数-1或者-2之后必定能够维护成3的倍数。
而这题,首先应该考虑到P点。
首先,第一个最小必败点就是3。根据前边的理论,假如这个n不是3的倍数,那么我们总可以让它减1或减2变成3的倍数,而作为3的倍数对手是不可能一次就拿完的说,这样一直下去…………………………最终先手者一定有机会把它变成3,或是后手者自杀,故意留给先手者2的k次幂,这样就也是胜利者,所以说
3的倍数就是递推出来的必败点。
只要判断n是否%3为0就好。(woc,斯巴达脸)
根本不是当初强行套公式变成的3*k/(pow(2,y)+1)….(好蠢)
代码:
#include
using namespace std;
int main()
{
int n;
while(cin>>n)
{
if(n%3)
cout <<"Kiki" <else
cout <<"Cici" <