﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Residence for sdfond-随笔分类-算法 - 其他</title><link>http://www.cppblog.com/sdfond/category/12472.html</link><description>世事洞明皆学问，人情练达即文章</description><language>zh-cn</language><lastBuildDate>Wed, 21 Apr 2010 10:56:52 GMT</lastBuildDate><pubDate>Wed, 21 Apr 2010 10:56:52 GMT</pubDate><ttl>60</ttl><item><title>环上的高斯消元问题 </title><link>http://www.cppblog.com/sdfond/archive/2010/02/06/107393.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Sat, 06 Feb 2010 10:07:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2010/02/06/107393.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/107393.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2010/02/06/107393.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/107393.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/107393.html</trackback:ping><description><![CDATA[【问题描述】<br>　　高斯消元法适用的两种情况为域上的问题和环上的问题。域上的问题就是可以通过加减乘除把系数阵化简成为对角线全1的形式，是允许有除法的，一般用于浮点数的高斯消元。而环上的问题一般涉及整数以及取模，除法是不允许的，此外环上的问题一般都要涉及高斯消元的一个比较难处理的问题：无穷解问题。<br><br>【问题分析】<br>　　首先考虑比较简单的环上的问题：模2问题，这类问题的经典代表是开关灯问题。其实这类问题可以允许除法（用异或代替），每次消元的时候如果出现不确定的变量，那么跳过当前列，保持行不变，继续消元。当消元过后会出现的问题是，如果系数阵的秩小于增光矩阵的秩，那么无解；或者不是所有的变量都已经取值，导致这个的原因一个是消元时出现全0列，一个是系数阵的秩等于增光矩阵的秩且小于未知数的个数，也就是出现无穷解。在模2域上出现无穷解的时候只需枚举每个不确定变元的值（0或1），一般是用来找到一个最优解。这里一个比较巧妙的方法是保留消元过程的对角矩阵，这样一旦确定了未知数，直接回带找解，无需重新建立方程。<br>　　模n域上的无穷解问题更为复杂一些。一个是变元的取值范围变大了（0到n-1，某些问题取值还会是负的），另一个问题是由于模n未必是素数，如果是素数存在解就一定唯一，不是素数的话会出现多组解，还得继续枚举才行。以几个题目为例：<br>　　POJ
2947 Widget
Factory：这是环上问题的基础版，考察了对于变元数和方程数不确定的时候对方程解数的判断方法。消元的过程还是很简单的，细节考虑清楚就可以了。<br>　　POJ
1395
Cog-Wheels：方程的建立很巧妙，由于数的范围很小（100以内），因此可以根据每个质因数的幂次建立方程！对每个轮子除以最小的那个数后就可以进行质因数分解，方程数很少；最后建立的是一个整系数方程。不过这里的问题是由于存在无穷解的情况，要搜索；而且变量的取值范围不太好把握，我是取增广阵的所有系数的最大值max，把枚举的界定在了|max|以内，有点像扩展欧几里德的思想，如果有x、y满足ax
+ by =
d，那么x上下浮动b个，y上下浮动a个依然方程成立。另外注意的是建立方程的时候会产生齐次方程，要特别判断一下。总而言之这个题目写起来很恶心，复杂度感觉巨高，但是实际运行速度很快。<br>　　POJ
2055 Kid's
Problem：这个题目BT程度又进了一步，是个模线性方程组，不仅可能存在无穷解，而且模不一定是素数，对于确定的变元取值也会很多，总之就是各种搜索。不过这个题目很无聊的一点是在消元过程中，之前我一直是取要消元的两个系数的最小公倍数，分别放大然后再减去，就像分数通分的做法，做其他的题目都没有问题（因为没有影响解的情况）；但是这个题目这样居然会超时，当然不是超时在高斯消元的过程，而是之后枚举的过程。这个题目必须利用那种类似求gcd的方法，两个方程互相减来减去，因为这个题目数据取值范围太小了（20以内），因此这样做的复杂度也不高。这两种做法的唯一区别就是后者消元后的对角阵中，主对角线的系数很小（减来减去减得很小），而用&#8220;通分&#8221;的方法系数会保留为原系数（可能很大），虽然最后计算的结果完全相同，但是可能后者能够快速得到一个好的可行解，利用这个剪掉了不少冗余情况，而前者也许差了一些，就超时了。<br>　　Ural 1561 Winnie the
Pooh：应该是高斯消元问题的终极版本了，考察的是对高斯消元的理解（不过没有在方程的建立上设置太多的坎）。这个题目可以归结为包含若干操作的动态高斯消元问题：添加一个变元，添加一个方程，询问给定方程解的情况。因为不是询问方程组的解，而是询问方程的解，这样的话有可能虽然有多组解但是最后对应方程的值是相同的。我一开始采用枚举方程的取值判断有解的方法，超时了；后来改成出现不确定解的时候搜索判断解的情况，依然超时。这两种方法的复杂度都达到O(n
^
3)以上，所以需要好的办法。仔细思考之后发现，如果方程有解且唯一，那么它一定和已经存在的方程组（看成是向量）是线性相关的，这样的话可以每次添加方程都维护对角阵，对于一次询问，利用已有的方程组依次对给定的方程消元，到最后判断这个方程的系数是否全0，如果是的话解个模方程就行了，如果不是的话说明这个方程的取值会有很多种情况。每次添加方程都判断是否产生矛盾（无解），如果无解以后不再判断，一直输出无解。利用这种方式可以很快的处理查询，每次复杂度才O(n
^
2)。<br><br>【问题总结】<br>　　环上的高斯消元问题应用比较广泛，但是编码的复杂度也比较高。此外，不同的题目往往要求各异，因此也没有统一的模板，需要根据题目的要求来编写程序。通过以上几个题目的练习，对于高斯消元的求解已经没有太大的问题了。但是题目中方程的建立以及优化求解依然是难点，需要不断地积累和总结。
<br><br>注：本文作于2009年7月3日20点整<br>     <img src ="http://www.cppblog.com/sdfond/aggbug/107393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2010-02-06 18:07 <a href="http://www.cppblog.com/sdfond/archive/2010/02/06/107393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SPOJ 2154 Kruskal</title><link>http://www.cppblog.com/sdfond/archive/2010/02/06/107365.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Sat, 06 Feb 2010 01:04:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2010/02/06/107365.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/107365.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2010/02/06/107365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/107365.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/107365.html</trackback:ping><description><![CDATA[【题目大意】<br>　　猴子和Kruskal玩一个取石子游戏，给定n堆石子，n不大于200，每堆石子的个数大于2小于2 ^
32，双方轮流取子，每次可以从一堆中取最多k个，当一方取完石子后某堆石子的个数是素数的话那么当前玩家获胜。问猴子是否有必胜策略。<br><br>【题目分析】<br>　　这是一道BT题，中间设置了许多trick。开始对题意没有完全理解，错了很多次，后来找来了数据，才发现了问题。<br>　　题目中描述的获胜策略是："A
player wins if after his move the size of some heap is a prime
number."这句话乍一看以为是取完石子后剩下的石子个数是素数的时候就获胜，其实还隐藏着另一种可能：如果多堆石子个数是素数，当前玩家无论怎样取都能获胜，因为在他取完之后，其他堆的石子个数是素数，也满足获胜条件。<br>　　接下来考虑一般情况。这个题目是限制中间状态的Nim游戏，也就是说，对于一堆个数为n的石子而言，它的SG值取决于小于n的最大素数。注意这里题设又有了一个小trick，题目说明了需要取1到k个，如果当前石子个数本身是素数，当然是没用的，因此是小于n的最大素数。设小于n的最大素数是p(题目中说明了石子个数大于2，因此p一定存在)，那么可以在k步以内到达p的一定是必胜态，而且是直接获胜，需要在输入的时候特判(这一点需要注意，在解决限制中间状态的Nim游戏时一般都需要特判)。然后就是p
+ k&nbsp;+ 1这个状态，因为它可达的状态全是必胜态，因此它是必败态，SG值为0。现在考虑大于p + k + 1的状态，问题出来了。以p + k +
2这个状态为例，因为它可以到达p + 2 ... p + k + 1这些状态，因为p + 2 ... p +
k都是直接获胜状态，如何判定他们的SG值呢？如果假设它们的SG值是1，那么p + k +
2这个状态的SG值应该是2。但是思考一下SG值的定义，它是定义在一个DAG上的，所有的状态最后都是可以在有限步内转移到终止态(必败态)。但是p + 1 ...
p + k这些状态都转移到了p这个状态上，我们肯定不能认定p状态是终止态，因此仅仅凭借p + 1 ... p +
k这些状态是必胜态就简单的把它们的SG值设为1是不恰当的；这些限制状态和以前的题目还不同，这些限制状态都不能转移到终止态上，但是由于题目的要求，它们又都是必胜态，因此把它们的SG值设为无穷大更合适些。<br>　　仔细思考一下带限制状态的SG游戏，可以发现，它们和一般的SG游戏的区别在于，在分析一般的SG游戏的时候，对于一个状态图而言，转移到终止态的时候并不意味着游戏结束，因为玩家可以通过走其他的状态图来保证是否达到必胜态；但是带限制状态的SG游戏，限制的状态双方都是不敢走的，因为一旦一方走入限制状态，另一方立刻获胜，游戏就终止了。可以认为，只要走入限制态就相当于认输，对于双方玩家而言肯定都不会这样做，因此这些限制状态就成了&#8220;死状态&#8221;，完全可以忽视这些状态（也就是说不存在到这些状态的转移）。通过上述分析，我们可以认定p
+ k +
2这个状态的SG值应该是1而不是2。<br>　　现在这个问题的做法就比较明朗了，对于每堆石子，去掉限制态的讨论后，就变成了在集合{1...k}中选择元素的一个Subtraction
Game，它的SG值是模(k +
1)循环的。<br>　　然后就是求最近素数的问题了，这个没有好办法，只能暴力枚举。对于一个数n，在sqrt(n)到n之间存在素数（感觉应该是，但是不会证），因此最多枚举sqrt(n)次就能找到解。但是每次枚举判素的复杂度还是sqrt(n)，总复杂度还是比较高，我在本地跑数据跑了3秒，交上去超时了（SPOJ的时限10秒，这居然也超时，数据够变态）。想了很久也没有什么新的算法，无奈只能在判素上下点功夫，直接把Miller-Rabin搞出来了，速度提高到了2秒，仍然超时。后来又加了一些常数上的优化，终于过了。<br><br>【总结】<br>　　这个题目做了很长时间，一方面是审题不清，错了几次；另一方面是对于SG的理论理解的不够透彻，想了很久终于想明白了；再有就是优化算法也花了很长时间。不过通过这个题目对于SG理论的理解又进了一步，感觉不错，呵呵。
<br><br>注：本文作于2009年8月6日 9点27分<br><br><img src ="http://www.cppblog.com/sdfond/aggbug/107365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2010-02-06 09:04 <a href="http://www.cppblog.com/sdfond/archive/2010/02/06/107365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>组合游戏总结——基本博弈问题</title><link>http://www.cppblog.com/sdfond/archive/2010/02/06/107364.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Sat, 06 Feb 2010 00:55:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2010/02/06/107364.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/107364.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2010/02/06/107364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/107364.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/107364.html</trackback:ping><description><![CDATA[<p>【概述】<br>　　最近的几次比赛，博弈的题目一直不少，而且博弈问题是一块比较复杂、庞大的内容，因此在这里小结一下，希望能够帮自己理清一些思路，争取也多来几个系列，呵呵。</p>
竞赛中出现的组合游戏问题一般都满足以下特征：<br>　　　　1.
二人博弈游戏，每个人都采用对自己最有利的策略，并且是两个人轮流做出决策<br>　　　　2.
在游戏中的任意时刻，每个玩家可选择的状态是固定的，没有随机成分<br>　　　　3.
游戏在有限步数内结束，没有平局出现<br>　　大部分的题目都满足上述条件，因此这里只讨论在上述条件范畴内的博弈问题。这类博弈问题，通常还有若干分类。一种是规定移动最后一步的游戏者获胜，这种规则叫做<span style="color: red;">Normal Play Rule</span>；另一种是规定移动最后一步的游戏者输，这种规则叫做<span style="color: red;">Misere Play
Rule</span>，也称为Anti-SG游戏。此外，对于游戏的双方，如果二者博弈的规则相同，那么称为这类游戏是<span style="color: red;">对等</span>(impartial games)的；否则称为<span style="color: red;">不平等游戏</span>(partizan games
)。当初WHU的那场比赛就是由于对于这个概念不是很清晰，导致看完题目之后就用SG定理来做，浪费了很多机时。实际上，解决不平等博弈问题的方法和普通的博弈问题（SG游戏）是有区别的，一般会采用动态规划或者surreal number。<br><br>【博弈基础知识】<br>　　在SG游戏中，最为人熟知的是必胜必败态，也叫NP态理论。注意的是，P态对应的是先手必败态，N态对应的是先手必胜态。必胜必败态理论是：<br>　　1. All terminal positions are
P-positions<br>　　2. From every
N-position, there is at least one move to a P-position<br>　　3. From every P-position, every move is to an
N-position<br>　　英文的表述非常简洁清晰，而且这个理论也很好理解，如果在当前状态的下一步可以走到必败态，那么当前玩家就可以走到那个状态，把必败态推给另一方；如果所有可达状态都是必胜态，那么当前玩家无论如何走，都会把必胜态让给对方。根据必胜必败态理论，我们可以递归的求出每个状态是N态还是P态。必胜必败态理论其实已经把博弈问题转化成了一个有向图，借助图这个模型来分析问题，使得问题变得形象了许多。需要注意的是，这种SG游戏对应的有向图是无环的，因为如果有环，那么游戏双方就可能在环上不停的转换状态，游戏不能在有限步终止，这样就不满足组合游戏的特征3了。<br>　　然而在很多时候仅仅知道某个状态是必胜还是必败是不够的，因为如果存在多个组合游戏（比如经典的Nim），对应的状态集合非常大，无法直接利用必胜必败态理论求解，因此需要用到博弈论中一个很重要的工具：SG函数。<br>　　某个状态的SG函数值定义为当前状态所有不可达的状态编号中最小的编号，其中终止态的SG函数值是0。有了这个工具，就引入一个非常强大的定理——SG分解定理：<br><br>　　<span style="background-color: #ffffff; color: red;">多个组合游戏的SG函数值是每个组合游戏的函数值的和。</span>（这里的和定义为异或操作）<br>　　<br>　　SG分解定理的证明不是很难，其实和Nim的证明很像。根据这个定理，我们就知道为什么Nim的解法是异或所有的石子个数了，因为每堆石子的SG值就是石子的个数。SG分解定理告诉我们任何SG游戏都可以转化成Nim游戏来做。<br>　　Nim中的一个变形就是拿走最后一块石子的人算输。通过修改SG的计算规则，可以得出相同的结论（因为当石子个数是1的时候SG值为0，因此要单独处理）；当然也可以利用一个叫做SJ定理的方法来做，依然是要处理当所有堆的SG值不大于1的情况。<br><br>【博弈基本模型】<br>　　除了Nim模型，很多模型都看似复杂，最后都划归到了Nim模型上，然后利用SG分解来做的。在证明两种模型等价的时候，可以通过计算SG值判断是否相同，或者通过判断必胜策略的走法将其转化为Nim。许多模型非常的神奇，其获胜策略又千差万别，因此无法一一列举，但是掌握一些经典模型是必须的，这样通过模型的转化可以简化问题的难度。<br>　　<span style="background-color: yellow;">经典模型1：Nim变种。</span>包括：<br>　　　　(1)
楼梯Nim。把奇数台阶的石子作为Nim，二者等价，因为必胜的策略是相同的。<br>　　　　(2) 每次可以取k堆，这个是经典的Moore
Nim。它是泛化的Nim游戏。<br>　　　　(3)
两堆石子，每次可以取一堆或两堆，从两堆取得时候个数必须相同，谁先取完获胜。这个是著名的威佐夫博弈，跟黄金分割数有关，具体证明不是很清楚，但是用SG值打表可以找出规律。代码如下：
<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">cstdio</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">cmath</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">algorithm</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">using</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;std;<br><br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;k&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(sqrt(</span><span style="color: #000000;">5.0</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2.0</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;a,&nbsp;b,&nbsp;t;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(scanf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%d&nbsp;%d</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">a,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">b)&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(a&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;b)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;swap(a,&nbsp;b);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;b&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;a;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(a&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">)(t&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;k))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts(</span><span style="color: #000000;">"</span><span style="color: #000000;">0</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts(</span><span style="color: #000000;">"</span><span style="color: #000000;">1</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>}</span></div>
<br>　　　　(4) Subtraction
Games。一种通用的Nim游戏，每次从可用状态集合中选择下一步的状态，有很多变形，核心思想还是计算SG函数值。<br>　　　　(5)
Take-and-Break Game。每次把局面分成多个Nim子游戏，利用SG分解定理求出对应的SG值。<br>　　<span style="background-color: yellow;">经典模型2：翻硬币游戏(Coin Turning
Game)</span><br>　　　　(1) 一维的翻硬币游戏，每次可以翻1个或两个。通过单独考虑每个可以翻的硬币发现，Coin Turning
Game的SG值和Nim等价，因此两个模型等价。需要注意的是，许多翻硬币游戏根据题目的要求，一般编号从0开始。<br>　　　　(2)
一维的翻硬币游戏，每次可以翻1个或两个，限定了翻第二枚硬币的范围，那么就和Subtraction Game等价了。<br>　　　　(3)
一维的翻硬币游戏，每次可以翻1个、2个或3个，这个游戏叫做Mock Turtles，有一个神奇的规律，是Odious Number序列。<br>　　　　(4)
高维的翻硬币游戏，需要用到Nim积和Tartan定理。<br>　　翻硬币模型的变化更多，很多模型都有一些奇妙的规律，需要打表才能发现。<br>　　<span style="background-color: yellow;">经典模型3：删边游戏(Green Hackenbush)</span><br>　　　　(1)
树的删边游戏：Colon原理证明这种模型和Nim依然是等价的，多个叉的SG值异或就是对应根节点的SG值。<br>　　　　(2)
无向图删边游戏：利用Fursion定理收缩圈，然后就转换成树的删边游戏了，不过这个定理还不会证。
<br><br>注：本文作于2009年8月3日 09点33分<br><br>  <img src ="http://www.cppblog.com/sdfond/aggbug/107364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2010-02-06 08:55 <a href="http://www.cppblog.com/sdfond/archive/2010/02/06/107364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PKU 3244 Difference between Triplets</title><link>http://www.cppblog.com/sdfond/archive/2009/07/14/89999.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Tue, 14 Jul 2009 02:34:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/07/14/89999.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/89999.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/07/14/89999.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/89999.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/89999.html</trackback:ping><description><![CDATA[【题目大意】<br>　　定义两个三元组I(xi, yi, zi)和J(xj, yj, zj)，他们的差为D(I, J) = max{xi - xj, yi - yj, zi - zj} - min{xi - xj, yi - yj, zi - zj}，给定n个三元组（n &lt;= 200000），求任意两个三元组的差的和。<br><br>【题目分析】<br>　　数据范围非常大，枚举必然不可，需要数学方法。这个题目巧妙之处在于，模型经过了层层的包装，要想一下子有想法还真不容易。既然不能枚举了，这个max和min操作就不好办了，应该设法去掉。max{a, b, c} - min{a, b, c} = |a - b| + |b - c| + | c - a| / 2，这个公式应该不难想到，但是这只是第一步，因为引进了绝对值，依然不好做。可以先算出分子，最后再除2。接下来需要一个等价变换，以a - b为例，a - b = xi - xj - yi + yj = (xi - yi) - (xj - yj)，同理把b - c、c - a都写成这种形式。这一步变换看似作用不大，但是假设我们算出所有的xi - yi之后（i = 0... n - 1），将其排序，会发现，对于第i个xi - yi，它前面的都比它小，后面的都比它大。而实际上，由于求任意两个三元组的差，肯定xi - yi会和任意的xj - yj都作差的，加了绝对值后，它对最后的结果就会贡献i个(xi - yi)，n - i - 1个-(xi - yi)。同样的方法算出所有的(yi - zi)和(zi - xi)，结果就能够求出来了。算法复杂度O(n * logn)。<br><br>【题目总结】<br>　　这是一道不错的题目，首先考察了公式的变形，需要改写max - min操作，之后的等价变换和排序的思想都非常值得借鉴。<br>题目代码：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><span style="color: #000000;">#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">cstdio</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">algorithm</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">using</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;std;<br></span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;N&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">200010</span><span style="color: #000000;">;<br><br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;x[N],&nbsp;y[N],&nbsp;z[N];<br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;n,&nbsp;a,&nbsp;b,&nbsp;c;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(scanf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%d</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">n)&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;n)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;n;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scanf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%d&nbsp;%d&nbsp;%d</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">a,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">b,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">c);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x[i]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;a&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;b;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y[i]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;b&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;c;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;z[i]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;c&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;a;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sort(x,&nbsp;x&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sort(y,&nbsp;y&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sort(z,&nbsp;z&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;ans&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;n;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ans&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">2</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;n)&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">)(x[i]&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;y[i]&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;z[i]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%I64d\n</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;ans&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>}<br></span></div><img src ="http://www.cppblog.com/sdfond/aggbug/89999.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-07-14 10:34 <a href="http://www.cppblog.com/sdfond/archive/2009/07/14/89999.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PKU 1723 SOLDIERS</title><link>http://www.cppblog.com/sdfond/archive/2009/06/25/88486.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Thu, 25 Jun 2009 02:13:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/06/25/88486.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/88486.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/06/25/88486.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/88486.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/88486.html</trackback:ping><description><![CDATA[<p>　　题目大意是给定n个点的坐标（n &lt;= 10000），问把这些点移动到一横行并且一个挨着一个（具体位置任意）的最少移动步数（其中每次只能向上下左右移动一个坐标）。<br>　　这个题目体现了转化的思想。首先考虑这样的问题：一个数轴上有n个坐标，问把这n个坐标移动到一个点上最少移动步数，其中每次移动一个格子。根据中位数的定义，把所有坐标排序后第n / 2个坐标是中位数，把所有坐标移动到这上面移动次数最小。证明很容易想到，因为如果不这样的话，把目标坐标往左平移还是往右平移，势必造成左半部的坐标集体变化1，右半部的坐标也集体变化1，如果左右半部坐标的个数不同，那么显然就不是最优的了。<br>　　接下来考虑题目，题目中x和y的移动是孤立的，可以分开讨论。y的移动方法和上面讨论的情况一样，现在考虑x的移动。x的移动要求最终是一个挨着一个的，x排好序之后，假设最终所有点以x0为左端点依次排开，对应的点分别为x0, x1...那么问题的答案就等于把这n个坐标依次对应的挪到x0到xn-1上的步数。如果我们把这n个目标点分别都移动到x0上，那么问题就转化成了中位数问题了。考虑把xi移动到x0上，要花费i步，为了保证问题是等价变换的，应该把xi在原坐标中对应的xi'也相应的向左移动i步，这样xi'移动到xi的代价就是不变的。设xi'左移i步后的新位置是xi''，那么问题就转化成：把x0''到xn-1''这n个点移动到一个坐标的最小步数，用中位数的方法就可以做出来了。<br>　　这个题目的巧妙之处在于把一个未知问题转化成一个已知问题。转化的思想在数学中用的很多，应该多多练习。<br><br>题目代码：<br></p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><span style="color: #000000;">#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">cstdio</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">algorithm</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">using</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;std;<br></span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;N&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">10010</span><span style="color: #000000;">;<br><br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;x[N],&nbsp;y[N],&nbsp;n;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(scanf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%d</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">n)&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;n;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scanf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%d&nbsp;%d</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">x[i],&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">y[i]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sort(x,&nbsp;x&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sort(y,&nbsp;y&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;n;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x[i]&nbsp;</span><span style="color: #000000;">-=</span><span style="color: #000000;">&nbsp;i;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sort(x,&nbsp;x&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;ans&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;n&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ans&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;x[n</span><span style="color: #000000;">-</span><span style="color: #000000;">i</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">]&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;x[i]&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;y[n</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">-</span><span style="color: #000000;">i]&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">&nbsp;y[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="color: #000000;">"</span><span style="color: #000000;">%d\n</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;ans);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>}<br></span></div><img src ="http://www.cppblog.com/sdfond/aggbug/88486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-06-25 10:13 <a href="http://www.cppblog.com/sdfond/archive/2009/06/25/88486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HOJ 2565 高斯消元</title><link>http://www.cppblog.com/sdfond/archive/2009/06/10/87357.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Wed, 10 Jun 2009 13:49:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/06/10/87357.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/87357.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/06/10/87357.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/87357.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/87357.html</trackback:ping><description><![CDATA[<p>&nbsp;　　这个题目做得好辛苦。题目大意是给三个算术表达式，前两个相加等于第三个，每位数都用字母代替，问最后字母对应的数是多少。数据范围n &lt;= 26。由于进位不确定，因此需要枚举进位，再用高斯消元求解。我报着试一试的态度敲了一个2 ^ n * n ^ 3的程序上去，果然超时了。不知道应该如何优化，到网上看了一下，因为每次枚举的都是常数，因此可以先把每个未知数用常量表示出来，这样每次枚举回带求解的复杂度就降到了O(n ^ 2)。感觉这种做法很巧妙，不过实现的时候出了很多问题，搞了一下午才搞定- -!<br>　　首先需要保存对于每个变量，它对应的每个常数的系数是多少。开始的时候我列方程的方向想错了，相当成模n域的方程组来求解。结果写了很久之后发现因为n不一定是素数，所以求解的时候解可能有多个，这样的话就比较复杂了。后来一怒之下索性当成实数域来求解，重新列了方程，这样解就是唯一的了，但是中间运算全是浮点数，很担心精度问题，交上去居然过了。<br>　　这个题目加速消元的思想还是很值得借鉴的，高斯消元的优化问题不多，但是感觉都挺有意思，就像用弦图加速高斯消元。根据方程的不同特点来选择合适的优化方法很重要啊。 </p><img src ="http://www.cppblog.com/sdfond/aggbug/87357.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-06-10 21:49 <a href="http://www.cppblog.com/sdfond/archive/2009/06/10/87357.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>博弈论的一些题目</title><link>http://www.cppblog.com/sdfond/archive/2009/05/31/86318.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Sun, 31 May 2009 13:53:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/05/31/86318.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/86318.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/05/31/86318.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/86318.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/86318.html</trackback:ping><description><![CDATA[先把题目贴出来，总结再说：<br>POJ 2234 Matches Game<br>HOJ 2533 Stone II<br>POJ 2975 Nim<br>HOJ 1367 A Stone Game<br>POJ 2505 A multiplication game<br>ZJU 3057 beans game<br>POJ 1067 取石子游戏<br>POJ 2484 A Funny Game<br>POJ 2425 A Chess Game<br>POJ 2960 S-Nim<br>POJ 1704 Georgia and Bob<br>POJ 1740 A New Stone Game<br>POJ 2068 Nim<br>POJ 3480 John<br>POJ 2348 Euclid's Game<br>HOJ 2645 WNim<br>POJ 3710 Christmas Game
<br>POJ 3533 Light Switching Game
<br> <img src ="http://www.cppblog.com/sdfond/aggbug/86318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-05-31 21:53 <a href="http://www.cppblog.com/sdfond/archive/2009/05/31/86318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高斯消元法</title><link>http://www.cppblog.com/sdfond/archive/2009/05/26/85820.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Tue, 26 May 2009 09:13:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/05/26/85820.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/85820.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/05/26/85820.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/85820.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/85820.html</trackback:ping><description><![CDATA[　　高斯消元法用于求解线性方程组，采用选主元的方法，算法复杂度O(N ^ 3)。相应的题型一种是在实数域进行求解，一种是在整数域求解，一般涉及到取模。实数域的求解比较简单，整数域需要注意几个问题。模p一定是素数，因为不是素数的话求解的时候可能会出现多个解，处理起来比较麻烦。一个特殊的情况是模2域下的求解，可以采用位运算优化。还有一点要注意的是在最开始构造系数矩阵和增广矩阵的时候，一定要先模p，否则选主元的时候一些模p为0的系数会被误选。<br>　　高斯消元法另一个需要讨论的地方就是解的情况。分为无解、唯一解和无穷解。这三种情况根据线性代数的知识很容易判断，主要就是看系数阵的秩和增广阵的秩。如果某一次选主元发现当前列的系数都为0，那么对应的变量是一个自由变元，解不确定。这个时候要跳过这一列，保持行不变，继续进行消元。在消元之后查看增广阵的秩确定是否无解，若有解再根据自由变元是否存在来判断是否是唯一解。<br>　　如果解是无穷多个的时候，需要枚举变元的取值。一般用于处理解空间很小的情况，比如在模2域上的求解。枚举变元后无需重新列方程，只需进行一次回带找解即可。 <img src ="http://www.cppblog.com/sdfond/aggbug/85820.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-05-26 17:13 <a href="http://www.cppblog.com/sdfond/archive/2009/05/26/85820.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>龙贝格积分</title><link>http://www.cppblog.com/sdfond/archive/2009/05/20/83506.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Wed, 20 May 2009 11:56:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/05/20/83506.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/83506.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/05/20/83506.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/83506.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/83506.html</trackback:ping><description><![CDATA[<p>　　龙贝格积分的基本思想就是先利用复化梯形公式把曲线划分若干小区间，把每个小区间当成梯形来求和；然后每次将区间数加倍，直到收敛到一定精度范围内为止。<br>　　程序基本参照计算方法的书写的，但是开始写完之后发现巨慢。找了网上一个版本和我的比较下，发现我们俩只是二维数组的两个维代表的含义互换了，但是时间复杂度完全相同。后来改了一下发现居然快很多，囧。之后可以过JOJ 2457。但是仍然过不了HOJ 2539。一旦把精度调高就TLE，郁闷。<br>　　后来找到了liuyu大牛曾经写过的romberg积分模板，发现巨快，研究了一下发现他把那个T数组巧妙的压缩成了一维，但是总时间复杂度不变，不知道为什么那么快，也许是因为二维数组遍历的时候寻址比较耗时间吧。按照他的方法改了下，仍然过不了，但是这回是WA。找了很久发现在更新的时候每次乘以定值就会WA，用pow才可以过，估计是数据的精度有问题。改了之后终于过了，速度很快:-)</p><img src ="http://www.cppblog.com/sdfond/aggbug/83506.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-05-20 19:56 <a href="http://www.cppblog.com/sdfond/archive/2009/05/20/83506.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>快速傅里叶变换</title><link>http://www.cppblog.com/sdfond/archive/2009/05/18/83287.html</link><dc:creator>sdfond</dc:creator><author>sdfond</author><pubDate>Mon, 18 May 2009 08:01:00 GMT</pubDate><guid>http://www.cppblog.com/sdfond/archive/2009/05/18/83287.html</guid><wfw:comment>http://www.cppblog.com/sdfond/comments/83287.html</wfw:comment><comments>http://www.cppblog.com/sdfond/archive/2009/05/18/83287.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sdfond/comments/commentRss/83287.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sdfond/services/trackbacks/83287.html</trackback:ping><description><![CDATA[　　对于两个n阶多项式的乘法，如果模拟做的话复杂度为O(n^2)，利用快速傅里叶变换可以把复杂度降到O(nlogn)。<br>　　多项式有两种表示：系数形式和点值表示。如果把两个多项式写成点值形式，那么相乘的复杂度就是O(n)的。FFT的基本思想就是通过把系数形式化成点值形式，相乘之后再化回来，使得复杂度降到O(nlogn)。具体就是先通过巧妙地选取n个复数单位根，利用复数的一些非常好的性质求得DFT，把这一步的复杂度降到O(nlogn)，然后将得到的点值相乘后，利用插值再变换成系数形式。插值的过程居然和求DFT的过程惊人的相似，复杂度依然为O(nlogn)。<br>　　在实现的时候基本参照算法导论，感觉递归不太好写，就写了个迭代的。N久不用复数了，连基本运算都忘了，导致实现的时候出了一堆错，后来好不容易写好了，结果却一点都不靠谱。查了好久才发现，初始w是1的时候，我把实部和虚部都设成1了，囧。实际上虚部应该是0。改完后发现多项式的表示又出了问题，后来发现我把系数的顺序写反了。然后利用这个做了HDU 1402，就是高精度乘法。WA了几次，很抓狂。后来写了一个程序跑了一组极限数据，居然挂了。仔细观察后发现是精度的问题。因为FFT中间运算过程都是浮点数，但是最后要输出整数，取整的时候舍入精度出了问题，加了1e-3之后过了。<br>　　还有一道比较巧妙的FFT的题目，SRM 436 DIV 1 1000pt，做的时候开始Z0忘记取模了，结果还以为是模板的问题，找了很长时间才发现。做题还是要细心啊。<img src ="http://www.cppblog.com/sdfond/aggbug/83287.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sdfond/" target="_blank">sdfond</a> 2009-05-18 16:01 <a href="http://www.cppblog.com/sdfond/archive/2009/05/18/83287.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>