??xml version="1.0" encoding="utf-8" standalone="yes"?>
先假设a2 = t, 题目l定?jin)递推关系QAn = 2 * t * An-1 - An-2 (n > 2)Q初值A1 = 1, A2 = tQ题目要求Sn = An ^ 2 + An-1 ^ 2 + ... + A1 ^ 2?br> 反复应用递推关系得到Q?br>
然后Sn-1利用相同的方式展开Q把4tAn-2An-3U去Q得刎ͼ(x)
q样比较容易得出Sn的通项Q?br>
当初q个题目之所以没惛_q种Ҏ(gu)Q就是因Z看到递推关系Q就想着用一般解方程的方法去求解通项Q思维被局限了(jin)Q其实用单的方式可以推出来的。以后对于一个问题,应该从多个方面去惻I不能惛_然啊。想hq看到的一D话Q以此自勉:(x)
正则表达式非常强大,但是它ƈ不能为每一个问题提供正的解决Ҏ(gu)。你应该学习(fn)_多的知识Q以辨别什么时候它们是合适的Q什么时候它们会(x)解决你的问题Q什么时候它们生的问题比要解决的问题还要多?br> 一些hQ遇C个问题时想Q?#8220;我知道,我将使用正则表达式?#8221;现在他有两个问题?sh)(jin)。——Jamie Zawinski
]]>
]]>
【题目分析?br> 对于盘放R问题可以用组合数学的知识来解冻I但是对于含禁区的摆放问题Q虽然组合数学给Z(jin)l典的棋盘多式+Ҏ(gu)原理的解法,但是实际中棋盘多式的求解是很困隄Q因此一般需要借助状态压~动态规划求解?br> 现在题目中要求出互不d的象的方法数Q象的攻击\U是斜的Q是不是可以考虑采用放R的方法来解呢Q将盘黑白染色Q如果一个象在黑色的格子里面Q那么它一定不?x)攻d白色的格子,q样的话可以分开计数Q然后最后利用乘法原理加hp?jin)。把盘旋{45度,q样象的d路线是直的?jin),如果只考虑一U颜色的话,那么问题p{变成?jin)经典的放R问题?sh)(jin),可以利用动态规划解冟?br> 设dp[i][j]表示前i行放?jin)j个R的方法数Qc[i]表示Wi行可以放|的子数量Q那么{ULEؓ(f)Q?br> dp[i][j]
= dp[i-1][j] + dp[i-1][j-1] * (c[i] - (j -
1))
需要注意的是c数组应该是增序的Q这h能保证前面的j-1行放?jin)RQ对应这一行就有j-1个位|不可放?jin)?br> q个题目的dp方程不难惻I但是如何把模型{化到放R问题是不Ҏ(gu)惛_的,其是将盘黑白染色后分开计数的想法,非常巧妙?br>
题目代码Q?
2 #include <algorithm>
3 using namespace std;
4 const int N = 70;
5
6 void init(int n, int c1[N], int c2[N])
7 {
8 memset(c1, 0, sizeof(int) * N);
9 memset(c2, 0, sizeof(int) * N);
10 for (int i = 1; i <= n; i++)
11 {
12 for (int j = 1; j <= n; j++)
13 {
14 if ((i + j) & 1)
15 c2[(i+j)/2]++;
16 else
17 c1[(i+j)/2]++;
18 }
19 }
20 }
21 void bishops(int n, int dp[N][N], int c[N])
22 {
23 for (int i = 0; i <= n; i++)
24 dp[i][0] = 1;
25 for (int i = 1; i <= n; i++)
26 for (int j = 1; j <= c[i]; j++)
27 dp[i][j] = dp[i-1][j] + dp[i-1][j-1] * (c[i] - j + 1);
28 }
29
30 int main()
31 {
32 int n, k, c1[N], c2[N], dp1[N][N], dp2[N][N], ans;
33
34 while (scanf("%d %d", &n, &k) == 2)
35 {
36 if (n == 0 && k == 0)
37 break;
38 init(n, c1, c2);
39 sort(c1 + 1, c1 + n + 1);
40 sort(c2 + 1, c2 + n);
41 memset(dp1, 0, sizeof(dp1));
42 memset(dp2, 0, sizeof(dp2));
43 bishops(n, dp1, c1);
44 bishops(n - 1, dp2, c2);
45 ans = 0;
46 for (int i = 0; i <= k; i++)
47 ans += dp1[n][i] * dp2[n-1][k-i];
48 printf("%d\n", ans);
49 }
50
51 return 0;
52 }
注:(x)本文作于2009q??3?19?1?br>
]]>
一? * 3的棋盘有3U摆法,一?*3的棋盘需要相互交错的摆放Q因此有2U摆法,其余依次cL?br> 但是q个递推方程对于q样的数据量肯定是无法接受的。将方程q行化简Q?br> f[n] = f[n-2] * 3 + (3 * f[n-4] + f[n-6] * 2 + ...) - f[n-4]
= f[n-2] * 3 + f[n-2] - f[n-4]
= 4 * f[n-2] - f[n-4]
q样p{化成?jin)线性递推方程Q可以用矩阵来做?jin)?br> 话说q个题目我在HOJ上做的时候因为数据小q接O(n^2)?jin),看来对于一个题目仔l思考、发散思维q是很重要的?br> 既然3*n的可以做Q那?*n应该也可以。后来发现居然还真有q个题:(x)POJ 3420?*n的递推方程为:(x)
f[n] = f[n-1] + 4 * f[n-2] + 2 * f[n-3] + 3 * f[n-4] + 2 * f[n-5] + 3 * f[n-6] + ...
= 5 * f[n-2] + 6 * f[n-3] + 5 * f[n-4] + 5 * f[n-5] + ...
= 5 * f[n-2] + (5 * f[n-3] + 6 * f[n-4] + 5 * f[n-5] + ...) + f[n-3] - f[n-4]
= 5 * f[n-2] + f[n-1] + f[n-3] - f[n-4]
后面的做法就一样了(jin)Q算法复杂度(4 ^ 3 * log n)?br>
]]>
【题目分析?br> q个题目乍一看挺单的Q但是想做对q是要仔l的思考下的。补集{化的思想Q求出所有共U的三元l,然后用L减掉是{案?jin),关键是如何求共U三元组。x坐标相同和y坐标相同的比较好计算Q在一条斜U的׃好算?jin),M囑֏玎ͼ即斜率相同的线Q经q的格点数可能各不相同。思\当然q是枚Dy / xQ不同的y / x定?jin)不同的矩Ş区域Q,之后如何有效的计,我采用的Ҏ(gu)可能有些ȝ(ch)Q有点类Ҏ(gu)的方法。以斜率为a / bZQ?a, b) = gQ那么m * n的区域内一定有(m - a + 1) * (n - b + 1)个那么大的矩形,q样的矩形经q的格点数是(g + 1)Q然后因为同{斜率小一点的矩Ş(a - a / g, b - b / g)也是存在的,个数同样可以l计出来Q但是有些大矩Ş包括?jin),要去掉;因此采用这U思想Q先求出大矩形的个数Q然后依ơ往下减Q就可以避免重复计数?jin)。虽然这样复杂度有点高,不过极限数据q是比较快的跑出来了(jin)?br> 说的可能不太清楚Q具体代码如下:(x)
2 #include <iostream>
3 using namespace std;
4 const int N = 1024;
5
6 bool tag[N][N];
7 long long calc(long long n)
8 {
9 if (n <= 2)
10 return 0;
11 return n * (n - 1) * (n - 2) / 6;
12 }
13 int gcd(int a, int b)
14 {
15 return b == 0 ? a : gcd(b, a % b);
16 }
17
18 int main()
19 {
20 int m, n, g, a, b, timer = 1, cnt[N], ta, tb;
21 long long ans;
22
23 while (scanf("%d %d", &m, &n) == 2)
24 {
25 memset(tag, 0, sizeof(tag));
26 if (m == 0 && n == 0)
27 break;
28 ans = calc((m + 1) * (n + 1)) - calc(m + 1) * (n + 1) - calc(n + 1) * (m + 1);
29 for (int i = m; i >= 1; i--)
30 for (int j = n; j >= 1; j--)
31 {
32 g = gcd(i, j);
33 a = i / g, b = j / g;
34 if (tag[a][b]) continue;
35 memset(cnt, 0, sizeof(cnt));
36 tag[a][b] = 1;
37 a = i, b = j;
38 ta = i / g, tb = j / g;
39 for (int k = g; k >= 2; k--)
40 {
41 cnt[k] += (m - a + 1) * (n - b + 1);
42 ans -= calc(k + 1) * cnt[k] * 2;
43 for (int t = 1; t <= k - 2; t++)
44 cnt[k-t] -= (t + 1) * cnt[k];
45 a -= ta, b -= tb;
46 }
47 }
48 cout << "Case " << timer++ << ": " << ans << endl;
49 }
50
51 return 0;
52 }
53
]]>
也就是一个容斥原理。然后ȝ期望E = sigma{C(n, i) * P(i) * i * n * d}Qi = 0 ... N?br> q个式子列出来后怎么也求不出l果。晚上imems告诉?jin)我一个很单的推导Ҏ(gu)。对于一个商店来_(d)一个h不去的概率是(N - 1) / NQ那么M个h都不ȝ概率?(N - 1) / N) ^ MQ用1减去q个l果是肯定臛_有h去这个商店的概率。然后ȝ期望׃?sh)N个商店,再乘?sh)赚钱数n * d可以了(jin)。很巧妙Q因Z是从商店的角度直接考虑的问题,而不考虑商店的h敎ͼq样׃用列概率分布?jin)?br> 但是上面的公式就不能推导出正结果了(jin)么,后来又推?jin)一下,发现是可以的Q照着l果? -!Q?br> 具体的推导很ȝ(ch)Q但是ȝ来说用到?jin)组合数学的几个公式。首?nbsp;k * C(n, k) = n * C(n - 1, k - 1)Q利用这个公式把外面的i消去。然后有pascal递推式:(x)C(n, k) = C(n - 1, k) + C(n - 1, k - 1)。我们分别考虑(i / N) ^ M前面的系敎ͼ可以发现都是两个二项式系数相乘的方式。把其中的一个利用pascal公式展开后,出现?jin)Ş如C(n, 0) - C(n, 1) + C(n, 2) - ...之类的式子,l果?Q消M(jin)。剩下的q是两个二项式系数的乘积Q不q都是这UŞ式的QC(n, k) * C(k, r)Q它{于C(n, r) * C(n - 1, k - 1)。这样变形之后有一个公共项可以提出去?jin),里面q是形如(1 - 1) ^ n的Ş式。这L(fng)果就?。在计算下一的pL的时候,W一ơ展开后里面恰好包含了(jin)前一的pLQ直接就?消去?jin),然后l箋(hu)利用上面的方法展开。中间推导的q程中还需要添加一些gؓ(f)0的项便于l箋(hu)的推对{?br> q个Ҏ(gu)很麻?ch),不过推导q程中还是用C(jin)很多知识的,当复习(fn)? -!其实q个推导要不是知道了(jin)最后的公式也不敢推Q实在太ȝ(ch)Q看来还是基本功Ơ缺啊,而且概率的题目q是要多l习(fn)l习(fn)。另外注意思维的灵zL,其实单做法不难想Q但是最开始被吉大牛误g(jin)Q就用了(jin)一个严格的推导Ҏ(gu)Q独立思考还是很重要的?br>
题目代码Q?br>
2 #include <cmath>
3
4 int main()
5 {
6 double N, M, n, d;
7
8 while (scanf("%lf %lf %lf %lf", &N, &M, &n, &d) == 4)
9 printf("%.3lf\n", n * d * N * (1.0 - pow(1.0 - 1.0 / N, M)));
10
11 return 0;
12 }
13
]]>
【算法分析?br> 感觉满恶?j)的一个题目,需要疯狂的找规律。首先容易看?gu)\径数是一个组合数Qƈ且每一层都是模六@环的。但是怎样扑ֈ层数Q也是最短\径长度)(j)呢?最开始想建立坐标pȝ后利用几何方法算出来Q但是如何无论是W卡?dng)坐标系q是极坐标系的徏立都是困隄Q然后想存图q搜Q发现空间不够。后来发玎ͼ把图时针{90度,出现?jin)一个很有意思的规律。以原点Q数字ؓ(f)1Qؓ(f)中心(j)建立坐标p,不过坐标的选取需要一些技巧。可以看出数字是一层层分布的,取x左边的点坐标为(-2Q?Q,以后每往左增加一层,横坐标就变化2。其实和原点U坐标相同且位于其左边的Ҏ(gu)好是每一层数字最大的l点Q这样只要确定了(jin)q个点,可以按照逆时针的序依次l这一层的所有点的坐标推出来。这Pl定一个数字,我们可以Ҏ(gu)每一层最大的数字推出q个数的坐标?br> 现在有了(jin)坐标Q可以看成是曼哈坐标)(j)Q就可以推测路径?jin)。把两个数字的坐标求差,可以看成是一个在原点?jin)。坐标和路径的关pM是很好找Q写??行发C(jin)一个很诡异的规律。先把坐标化成正的,然后发现x >= y的时候就是C( (x + y) / 2, y)Q否则是C( y, (y - x) / 2)。之后就是高_ֺ?jin)?br>
题目代码Q?br>
2 import java.math.*;
3
4 class point
5 {
6 int x, y;
7 public point(int x, int y)
8 {
9 this.x = x;
10 this.y = y;
11 }
12 }
13 class Main
14 {
15 public static void main(String[] args)
16 {
17 Scanner in = new Scanner(System.in);
18 int a, b, len;
19 BigInteger ans;
20 point pa, pb;
21
22 while (in.hasNext())
23 {
24 a = in.nextInt();
25 b = in.nextInt();
26 if (a == 0 && b == 0)
27 break;
28 pa = GetCoordinate(a);
29 pb = GetCoordinate(b);
30 pa.x = pb.x - pa.x;
31 pa.y = pb.y - pa.y;
32 pa.x = pa.x < 0 ? -pa.x : pa.x;
33 pa.y = pa.y < 0 ? -pa.y : pa.y;
34 if (pa.x >= pa.y)
35 {
36 len = (pa.x + pa.y) / 2;
37 ans = C(len, pa.y);
38 }
39 else
40 {
41 len = pa.y;
42 ans = C(len, (pa.y - pa.x) / 2);
43 }
44 System.out.print("There ");
45 if (ans.compareTo(BigInteger.ONE) == 0)
46 System.out.print("is 1 route");
47 else
48 System.out.print("are " + ans + " routes");
49 System.out.println(" of the shortest length " + len + ".");
50 }
51 }
52 static BigInteger C(int n, int k)
53 {
54 BigInteger ret = BigInteger.ONE;
55 if (k > n - k)
56 k = n - k;
57 for (int i = 1; i <= k; i++)
58 {
59 ret = ret.multiply(new BigInteger(new String(n - i + 1 + "")));
60 ret = ret.divide(new BigInteger(new String(i + "")));
61 }
62 return ret;
63 }
64 static point GetCoordinate(int n)
65 {
66 int[][] dir = new int[][]{{1, -1}, {2, 0}, {1, 1}, {-1, 1}, {-2, 0}, {-1, -1}};
67 int i = 0, j = 0, k = 0, tx, ty, cnt = 0, delta;
68 point p = new point(0, 0);
69
70 if (n == 1)
71 return p;
72 for (i = 2; i <= 1000; i++)
73 if ((3 * i * i - 3 * i + 1) >= n)
74 break;
75 delta = 3 * i * i - 3 * i + 1 - n;
76 i--;
77 tx = -2 * i;
78 ty = 0;
79 boolean flag = false;
80 for (j = 0; j < 6; j++)
81 {
82 for (k = 0; k < i; k++)
83 {
84 if (cnt == delta)
85 {
86 flag = true;
87 break;
88 }
89 cnt++;
90 tx += dir[j][0];
91 ty += dir[j][1];
92 }
93 if (flag)
94 break;
95 }
96 p.x = tx;
97 p.y = ty;
98
99 return p;
100 }
101 }
102
]]>
const int N = 10010;
int main()
{
int a[N], n, mod[N] = {0}, tmp = 0, len = 0, pos;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
if (len) continue;
tmp = (tmp + a[i]) % n;
if (tmp == 0)
{
len = i;
pos = 1;
}
if (mod[tmp])
{
len = i - mod[tmp];
pos = mod[tmp] + 1;
}
else
mod[tmp] = i;
}
printf("%d\n", len);
for (int i = 0; i < len; i++)
printf("%d\n", a[pos+i]);
return 0;
}
]]>
]]>
2 const int N = 12;
3
4 int p[N], ans;
5 long long gcd(long long a, long long b)
6 {
7 return b == 0 ? a : gcd(b, a % b);
8 }
9 void calc_sg(long long tol, int m, int dep, long long v, bool mark)
10 {
11 if (dep == m)
12 {
13 if (mark) ans -= tol / v;
14 else ans += tol / v;
15 return;
16 }
17 calc_sg(tol, m, dep + 1, v, mark);
18 if (v <= tol)
19 calc_sg(tol, m, dep + 1, v / gcd(v, p[dep]) * p[dep], mark ^ 1);
20 }
21
22 int main()
23 {
24 int T, m, n, a, g;
25
26 scanf("%d", &T);
27 while (T--)
28 {
29 g = 0;
30 bool mark = 1;
31 scanf("%d %d", &m, &n);
32 for (int i = 0; i < m; i++)
33 scanf("%d", &p[i]);
34 for (int i = 0; i < n; i++)
35 {
36 scanf("%d", &a);
37 if (!mark) continue;
38 ans = 0;
39 calc_sg(a, m, 0, 1, 0);
40 g ^= ans;
41 for (int j = 0; j < m; j++)
42 if (a % p[j] == 0)
43 {
44 mark = 0;
45 break;
46 }
47 }
48 if (!mark) puts("Xiao Hong");
49 else printf("%s\n", g ? "Xiao Hong" : "Xiao Gang");
50 }
51
52 return 0;
53 }
54
]]>
polya题目Q?br>HOJ 2084 The Colored Cubes
HOJ 2647 Megaminx
POJ 1286 Necklace of Beads
POJ 2409 Let it Bead
TOJ 2795 The Queen's New Necklaces
HDU 1812 Count the Tetris
UVa 11255 Necklace
POJ 2154 Color
POJ 2888 Magic Bracelet
UVa 10601 Cubes
NUAA 1110
]]>
其中A =(Ak-1 Ak-2 ... A1)QI是单位矩c(din)?br>然后构造一个k * 1列向量bQ?br>
q样QM * b之后b0的值就是f(k)Q以此类推,M ^ n * b之后b0的值就是f(k-1+n)Q算法复杂度O(k ^ 3 * logn)?br>
]]>
1.排列l合与容斥原?br>排列l合里面?个重要的基本原理Q加法原理、乘法原理、减法原理、除法原?br>前面两个最为基本,后面两个是根据前两个z出来的。乘法原理有的时候的应用很y妙,可以作ؓ(f)一U打开思\的办法?br>基本的排列组合之后,接下来引Z(jin)多重集。多重集的排列组合是一个很l典的问题,ȝ如下Q?br>多重集的排列Q?br> 全排列的话只需应用除法原理可以了(jin)。n个元素的多重集的r排列需要利用指数生成函数来做?br>多重集的l合Q?br> n个元素的多重集的rl合Q如果r于{于M一个元素可选的个数Q那么就归结为经典的不定方程的解数问题,可以利用“隔板?#8221;来做。结果就是一个组合数。如果r大于某些元素的可选个敎ͼ那么一U方法是利用Ҏ(gu)原理Q一U方法还是要依靠生成函数Q编E序的时候可以用动归做)(j)?br>如果是一个环形的排列l合Q那么问题就困难许多Q要利用|换和polya定理?br> 单纯的依靠四基本原理来计数Q有的时候会(x)昑־力不从心(j)Q这个时候就需要容斥原理的帮助。容斥原理特别适合解决若干限制条g的交、ƈ问题Q也是打开思\的一U方法?br> 利用Ҏ(gu)原理解决的经兔R题有Q错排问题,带禁止位|的排列。禁位排列总觉得用Ҏ(gu)原理解决的不够优,不知道有没有可以~程的数学方法。还有一个困惑的问题是Ҏ(gu)原理和mobius反演的关p,那个地方好晦涩。?br> 跟排列组合相关的q有是生成排列和组合。生成排列利用那个什么字典序法好像够了(jin)Q编E好实现。生成组合方法类伹{?br>
2.二项式定?br>有很多公式,用的时候可以现查。终于知道了(jin)三角形数原来跟排列组合有养I而且是一个很z的公式?br>很多公式的推导用的思想很妙。有一个很好的思想是?1 + x) ^ n利用二项式定理展开Q然后求对{求U分Q居然可以导出很多不可思议的公式?br>q有一个很重要的定理就是pascal定理Qpascal递推式很有用Q展开后有两种形式Q一U是上下限均不定Q一U是下限不定Q,可以解决很多l合数的求和问题?br>另外一个重要的定理是牛顿二项式定理,在生成函C应用q泛Q可是推导h有点J?br>
3.递推关系和生成函?br> 求解U性递推关系的特征方E的Ҏ(gu)q是有一定h(hun)值的Q但是编E不适用。n解线性齐ơ递推方程有矩阵解法。稍微复杂点的递推关系Q非U性)(j)Q特征方E就不够用了(jin)Q必ȝ出生成函数这个有力的武器。感觉生成函数实在是太优、太强大?jin)。生成函数的关键是要把多项式拆分成(1-rx)^nq种形式Q这样就可以利用牛顿二项式定理展开?jin)?br> 在特D计数序列里面提C(jin)盒装球问题。将p个不同的球放入k个相同的盒子Q每个盒子非I)(j)的方法数是第二类Stirling数S(p, k)Q将p个相同的球放入k个相同的盒子Q每个盒子非I)(j)的方法数是分拆数Q可以归lؓ(f)整数划分问题Q用动态规划求解;p个不同的盒子攑օ不同的k个盒子ƈ且每个非I的Ҏ(gu)Cؓ(f)k! * S(p, k)?br> 有几个很l典的递推关系Q斐波那契数列、Catalan敎ͼ几种l典的Ş式:(x)三角剖分数、二叉生成树(wi)个数?1-1序列、加括号序列{等Q、Stirling敎ͼ两种Q第二种比较常用Q、汉诺塔、n个圆切割q面数、n条直Uk个交点切割^面数{等。此外,D\径中提到的^UR反和一一对应q三U分析问题的Ҏ(gu)也很值得借鉴?br>
4.polya定理