Northwestern Europe 2005 Unequalled Consumption求最小的q, 使方程a1*x1+a2*x2+...+an*xn=q的整数解大于给定的数。
n<=5, ai <= 10,q <= 10^15
设该方程的解数是f(q),则f(q)未必单调,但对确定的q0, f(q0+t*a1)必然关于t单调(因为t小的时候的所有的解都可以平行移动到大的t的解)
然后枚举下q0(反正才10个),二分。
问题的关键就是求f(q)
利用母函数,f(q)就是母函数
P(x) = (1 + x^a1 + x^(2*a1) + x^(3*a1) +...)(1 + x^a2 + x^(2*a2) + ...)...(1 + x^an + x^(2*an) + ...)
的x^q项系数。
令LCM为a1, a2...an的最小公倍数。
则P(x) =
(1 + x^a1 + x^(2*a1) + ... + x^(LCM - a1)) * (1 + x^LCM + x^(2 * LCM) + ...)*
(1 + x^a2 + x^(2*a2) + ... + x^(LCM - a2)) * (1 + x^LCM + x^(2 * LCM) + ...)*
*...*
(1 + x^an + x^(2*an) + ... + x^(LCM - an)) * (1 + x^LCM + x^(2 * LCM) + ...)
=
(1 + x^a1 + x^(2*a1) + ... + x^(LCM - a1)) * (1 + x^a2 + x^(2*a2) + ... + x^(LCM - a2)) * ... *(1 + x^an + x^(2*an) + ... + x^(LCM - an)) *
(1 + x^LCM + x^(2 * LCM) + ...)^n
注意前一部分的x^r系数可以算出来(例如用DP)
后一项中x^(LCM * k)的系数是C(n+k-1, n-1) (推一下就知道了)
然后就可以得出结果了是吧~~~
p.s.
利用推出的结论可以知道对于给定的r, f(q + LCM * t)是一个关于t的n次函数。。。所以其实可以算出所有小于n * LCM的f(q)值然后使用Langrage插值公式,这个是标程的做法。(其实我想知道有没有更简单的方法证明这是一个关于t的n次函数~)
SPOJ 598, INCR求n的排列中,最长上升序列为b的排列个数。
n<=40, b <= 5
做法是dp, 状态记录n的排列中len = 2的上升序列最后元素的最早位置,len = 3的上升序列最后元素的最早位置...
每次转移的时候枚举n + 1的放置位置,并计算新的状态。
由于有效状态的稀疏性,可以用map / hash来优化。。。

CODE
1
// SPOJ598, INCR
2
// Written By FreePeter
3
4
#include <cstdio>
5
#include <cstring>
6
#include <iostream>
7
#include <map>
8
9
using namespace std;
10
11
const int MaxN = 40 + 5, MOD = 1000000000;
12
map<int, int> f[MaxN];
13
14
void all_init();
15
bool judge(int id, int b);
16
int encode(int stat[4]);
17
void decode(int id, int stat[4]);
18
void make_newstat(int stat[4], int pos, int new_stat[4]);
19
20
int main()
{
21
all_init();
22
23
int t;
24
cin >> t;
25
for (; t > 0; --t)
{
26
int ans = 0;
27
int n, b;
28
cin >> n >> b;
29
for (map<int, int>::iterator it = f[n].begin(); it != f[n].end(); ++it)
{
30
if (judge(it->first, b))
{
31
ans += it->second;
32
if (ans >= MOD) ans -= MOD;
33
}
34
}
35
36
cout << ans << endl;
37
}
38
39
return 0;
40
}
41
42
void all_init()
{
43
int stat[4] =
{50, 50, 50, 50};
44
f[1][encode(stat)] = 1;
45
for (int i = 1; i < 40; ++i)
{
46
for (map<int, int>::iterator it = f[i].begin(); it != f[i].end(); ++it)
{
47
decode(it->first, stat);
48
49
int new_stat[4];
50
for (int j = 0; j <= i; ++j)
{
51
if (j > stat[3]) break; // It can make an permutation whose degree will exceed 5
52
53
make_newstat(stat, j, new_stat);
54
int &val = f[i + 1][encode(new_stat)];
55
val += it->second;
56
if (val >= MOD) val -= MOD;
57
}
58
}
59
}
60
}
61
62
bool judge(int id, int b)
{
63
int stat[4];
64
decode(id, stat);
65
for (int i = 0; i < b - 1; ++i)
66
if (stat[i] == 50) return false;
67
for (int i = b - 1; i < 4; ++i)
68
if (stat[i] != 50) return false;
69
70
return true;
71
}
72
73
int encode(int stat[4])
{
74
int ans = 0;
75
for (int i = 0; i < 4; ++i)
76
ans = (ans << 6) | stat[i];
77
return ans;
78
}
79
80
void decode(int id, int stat[4])
{
81
for (int i = 3; i >= 0; --i)
{
82
stat[i] = id & 63;
83
id >>= 6;
84
}
85
}
86
87
void make_newstat(int stat[4], int pos, int new_stat[4])
{
88
for (int i = 0; i < 4; ++i)
{
89
if (stat[i] < pos) new_stat[i] = stat[i];
90
else
{
91
if (i == 0) new_stat[i] = (0 < pos ? pos : min(stat[i] + 1, 50));
92
else new_stat[i] = (stat[i - 1] < pos ? pos : min(stat[i] + 1, 50));
93
}
94
}
95
96
}
97
Dhaka 2007 The Dumb Grocer
题意懒得说了~
首先要有1是吧。。。然后我们按照1的个数来分类,我们来计算恰有k个1的方案数。
我们在k个1的基础上加入新的数,显然第一个数只能是k+1
然后加入的数只能是k + 1 or 2 * (k + 1)
如法炮制。。。发现非1的数都具有(k + 1) * t的形式。。。设其依次为(k + 1) * ti
则{ti}这些数也满足题目的性质。。。共有f((n - k) / (k + 1))种方案。
设f(n)是要求的函数,则f(n) = sigma(f((n - k) / (k + 1)), (k + 1) | (n + 1) , k>=1
f(0) = 1
这样直接做会T...
我们令g(n) = f(n - 1)
则g(n) = f(n - 1) = sigma(f((n - k - 1) / (k + 1))) = sigma(f(n / (k + 1) - 1)) = sigma(g(n / (k + 1)), (k + 1) | n, k >= 1
设n = p1^a1 * p2^a2 * ... * pr*ar
令h(p1, p2,.., pr, a1, a2...ar) = g(n)
= h(p1,p2, ...pr, b1, b2, ...br),
0<=bi <= ai, bi不全=ai
注意对于一个确定的n,h()中的p1, p2...pr在计算过程中始终不变。。。所以。。。
计算结果与pi无关,只与ai有关这样状态数就大大减少了。。。直接因式分解后dp就行了。。。

CODE
1
// Dhaka 2007 Problem C, The Dumb Grocer
2
// Written By FreePeter
3
4
#include <cstdio>
5
#include <cstring>
6
#include <iostream>
7
#include <map>
8
#include <algorithm>
9
#include <limits>
10
11
using namespace std;
12
13
const int MaxN = 100000, MaxINT = numeric_limits<int>::max();
14
map<long long, long long> f;
15
long long fact[11];
16
int prime[MaxN / 8], prime_cnt = 0;
17
bool is_prime[MaxN + 10];
18
19
long long encode(int stat[10]);
20
void decode(long long id, int stat[10]);
21
long long calc(long long id);
22
long long generate_all(int deep, int stat[10], int cur_stat[10]);
23
void make_prime();
24
25
int main()
{
26
make_prime();
27
28
fact[0] = 1;
29
for (int i = 1; i <= 10; ++i)
30
fact[i] = fact[i - 1] * i;
31
32
f[0] = 1;
33
int t;
34
cin >> t;
35
for (int testno = 0; testno < t; ++testno)
{
36
int n;
37
cin >> n;
38
if (n == MaxINT)
{
39
cout << "Case " << testno + 1 << ": " << n / 2 + 1 << endl;
40
continue;
41
}
42
++n;
43
44
int e[10] =
{0}, e_cnt = 0;
45
for (int i = 0; prime[i] * prime[i] <= n; ++i)
{
46
if (n % prime[i] == 0)
{
47
int exp = 1; n /= prime[i];
48
for (; n % prime[i] == 0; n /= prime[i]) ++exp;
49
e[e_cnt++] = exp;
50
}
51
}
52
if (n != 1) e[e_cnt++] = 1;
53
54
sort(e, e + 10);
55
cout << "Case " << testno + 1 << ": " << calc(encode(e)) << endl;
56
}
57
58
return 0;
59
}
60
61
long long encode(int stat[10])
{
62
long long ans = 0;
63
for (int i = 0; i < 10; ++i)
64
ans = (ans << 6) | stat[i];
65
return ans;
66
}
67
68
void decode(long long id, int stat[10])
{
69
for (int i = 9; i >= 0; --i)
{
70
stat[i] = id & 63;
71
id >>= 6;
72
}
73
}
74
75
long long calc(long long id)
{
76
map<long long, long long>::iterator it = f.find(id);
77
if (it != f.end()) return it->second;
78
79
int stat[10], cur_stat[10];
80
decode(id, stat);
81
long long ans = generate_all(0, stat, cur_stat);
82
return f[id] = ans;
83
}
84
85
long long generate_all(int deep, int stat[10], int cur_stat[10])
{
86
if (deep == 10)
{
87
if (memcmp(cur_stat, stat, sizeof(int) * 10) == 0) return 0;
88
89
long long ans = 1;
90
int ptr = 9;
91
for (int i = 9; i >= 0; --i)
{
92
for (; ptr >= 0 && stat[ptr] >= cur_stat[i]; ) --ptr;
93
int fac = 9 - ptr;
94
fac -= (9 - i);
95
96
ans *= fac;
97
}
98
99
ptr = 0;
100
for (; ptr < 10; )
{
101
int new_ptr = ptr;
102
for (; new_ptr < 10 && cur_stat[new_ptr] == cur_stat[ptr]; ) ++new_ptr;
103
ans /= fact[new_ptr - ptr];
104
ptr = new_ptr;
105
}
106
107
return ans *= calc(encode(cur_stat));
108
}
109
110
long long ans = 0;
111
for (int i = (deep == 0 ? 0 : cur_stat[deep - 1]); i <= stat[deep]; ++i)
{
112
cur_stat[deep] = i;
113
ans += generate_all(deep + 1, stat, cur_stat);
114
}
115
116
return ans;
117
}
118
119
void make_prime()
{
120
fill(is_prime, is_prime + MaxN + 10, true);
121
for (int i = 2; i <= MaxN; ++i)
122
if (is_prime[i])
{
123
prime[prime_cnt++] = i;
124
if (i >= 1000) continue;
125
for (int j = i * i; j <= MaxN; j += i)
126
is_prime[j] = false;
127
}
128
}
129
130
Dhaka 2007 You are around me ...首先旋转坐标,变成平行与xy轴的椭圆,然后坐标伸缩。。。变成圆。。。最近点对。。。贴模板。。。
ZJU2107 Quoit Design 一道测最近点对的题。
Dhaka 2007 Magnetic Train Tracks给定n个点,求可以构成多少个锐角三角形。
n <= 1200
话说求锐角三角形不太好算是吧。。。补集转换,我们来求钝角/直角三角形 <=> 求钝角/直角个数。。。
后面的事情就简单了,是对每个点,将其他点按照极角排序 + 扫描。
Dhaka 2005 Counting Triangles 也是一道补集转换的题~(转化成求三点共线的个数)
Shanghai 2004 Amphiphilic Carbon Molecules 也是一道极角排序+扫描的题。