0%

链接

传送门

题意

\(n\)个电脑,\(m\)个插口,每个电脑有一个需求能量\(p_i(1 \leq p_i \leq 10^9)\),每个插口有一个输出能量\(s_j(1 \leq s_j \leq 10^9)\)。当\(p_i=s_j\)时,电脑\(i\)和插口\(j\)可以相连。使用适配器可以将插口的输出能量减少至原来的一半(向上取整)。输出最多能使多少组电脑和插口配对,和此时使用适配器的数量的最小值。

思路

用multiset存电脑。对于每个插口最多接\(\log n\)个适配器,从输出能量小的插口开始枚举安装适配器的个数看能否和电脑配对。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define X first
#define Y second

using namespace std;

typedef pair<int, int> pii;

const int maxn = 200010;

int a[maxn], b[maxn];
pii s[maxn];
multimap<int, int> num;
multimap<int, int>::iterator it;

int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1, x; i <= n; ++i) {
scanf("%d", &x);
num.insert(make_pair(x, i));
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &s[i].X);
s[i].Y = i;
}
sort(s + 1, s + m + 1);
int c = 0, u = 0;
for (int i = 1; i <= m; ++i) {
for (int j = 0; ; ++j) {
it = num.find(s[i].X);
if (it != num.end()) {
a[s[i].Y] = j;
b[it -> Y] = s[i].Y;
++c;
u += j;
num.erase(it);
break;
}
if (s[i].X == 1) {
break;
}
s[i].X -= s[i].X >> 1;
}
}
printf("%d %d\n", c, u);
for (int i = 1; i <= m; ++i) {
printf("%d ", a[i]);
}
puts("");
for (int i = 1; i <= n; ++i) {
printf("%d ", b[i]);
}
puts("");
return 0;
}

链接

传送门

题意

\(n\)天,\(m\)个科目,给出每天可以考试的科目和考过一个科目之前需要复习的天数,输出最少多少天可以考过所有科目。

思路

二分完成的天数,然后每个科目,如果有多个日期可以参与考试,选后面的日期考试,不会比选前面的更差。然后用扫描线检验一遍中途有没有复习时间不够的就好啦。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 100010;

int n, m;
int d[maxn], a[maxn], tmp[maxn];
int vis[maxn], vis_clock;

bool check(int x) {
++vis_clock;
int cnt = 0;
for (int i = x; i > 0; --i) {
if (d[i] != 0 && vis[d[i]] != vis_clock) {
tmp[i] = -a[d[i]];
vis[d[i]] = vis_clock;
++cnt;
} else {
tmp[i] = 1;
}
}
if (cnt != m) {
return false;
}
int sum = 0;
for (int i = 1; i <= x; ++i) {
sum += tmp[i];
if (sum < 0) {
return false;
}
}
return true;
}

int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &d[i]);
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &a[i]);
}
int L = m, R = n, ans = inf;
while (L <= R) {
int M = L + (R - L) / 2;
if (check(M)) {
R = M - 1;
ans = min(ans, M);
} else {
L = M + 1;
}
}
printf("%d\n", ans == inf ? -1 : ans);
return 0;
}

链接

传送门

题意

如图,给出在格点上的凸多边形,输出面积最大的边与坐标轴平行的矩形的面积。

思路

首先,最大矩形的面积为矩形宽度的上凸函数。然后,对于指定宽度的矩形,矩形的面积为左边界的位置的上凸函数。给出指定的位置可以求出当前位置与多边形交点的\(y\)值。因此可以使用三分套三分套二分的方式解决这道题。

UVa 1679 - Easy Geometry和这个是同一道题,不过好像题的数据有问题,NEERC官方题解中tourist的代码也过不了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 100010;
const int maxt = 50;

int n, uNum, dNum;
int x[maxn], y[maxn];
double ans, h;
double Min, Max;
double tmpya, tmpyb;
double xa, xb, ya, yb;
double ux[maxn], uy[maxn];
double dx[maxn], dy[maxn];

double calY(double* vx, double* vy, int num, double x) {
int p = int(upper_bound(vx, vx + num, x) - vx);
return vy[p - 1] + (vy[p] - vy[p - 1]) / (vx[p] - vx[p - 1]) * (x - vx[p - 1]);
}

double calH(double x, double w) {
tmpya = max(calY(dx, dy, dNum, x), calY(dx, dy, dNum, x + w));
tmpyb = min(calY(ux, uy, uNum, x), calY(ux, uy, uNum, x + w));
double h = tmpyb - tmpya;
return h > 0 ? h : 0;
}

double calS(double w) {
double lx = Min, rx = Max - w;
for (int i = 0; i < maxt; ++i) {
double xx1 = (lx * 2 + rx) / 3;
double xx2 = (lx + rx * 2) / 3;
double s = calH(xx2, w);
if (s == 0.0 || s < calH(xx1, w)) {
rx = xx2;
} else {
lx = xx1;
}
}
double res = calH((lx + rx) / 2, w) * w;
if (res > ans) {
ans = res;
xa = (lx + rx) / 2;
xb = xa + w;
ya = tmpya;
yb = tmpyb;
}
return res;
}

void gen(double* vx, double* vy, int& num, int p, int d, int k) {
while (x[(p + n + d) % n] == Min) {
p = (p + n + d) % n;
}
for (num = 0;;) {
vx[num] = x[p];
vy[num] = y[p];
++num;
if (x[p] == Max) {
return;
}
p += d;
if (p == k) {
p = (p + n) % n;
}
}
}

int main() {
while (~scanf("%d", &n)) {
Min = inf, Max = -inf;
int p = 0;
for (int i = 0; i < n; ++i) {
scanf("%d%d", &x[i], &y[i]);
if (x[i] < x[p]) {
p = i;
}
Min = min(Min, double(x[i]));
Max = max(Max, double(x[i]));
}
gen(ux, uy, uNum, p, 1, n);
gen(dx, dy, dNum, p, -1, -1);
double lw = 0, rw = Max - Min;
ans = 0;
for (int i = 0; i < maxt; ++i) {
double w1 = (lw * 2 + rw) / 3;
double w2 = (lw + rw * 2) / 3;
double s = calS(w2);
if (s == 0.0 || s < calS(w1)) {
rw = w2;
} else {
lw = w1;
}
}
printf("%.10f %.10f %.10f %.10f\n", xa, ya, xb, yb);
}
return 0;
}

链接

传送门

题意

\(n\)个仓库,\(m\)个管理员,每个管理员有一个能力值\(p[i]\),雇佣管理员的花费与能力值相同。一个仓库的安全度为\(p[i]/k\),其中\(k\)为管理员管理仓库的个数,求最小安全度的最大值和此时的花费。

思路

定义状态\(dp[i][j]\)为前\(i\)个管理员管理前\(j\)个仓库时的最小安全度的最大值。有\(dp[i][j]=\max\{\min\left(dp[i-1][j-k],p[i]/k\right)\}\)。求花费的方法与之类似。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 110;

int p[maxn];
int dp[maxn][maxn];

int main() {
int n, m;
while (~scanf("%d%d", &n, &m) && (n || m)) {
for (int i = 1; i <= m; ++i) {
scanf("%d", &p[i]);
}
memset(dp, 0, sizeof dp);
for (int i = 1; i <= m; ++i) {
dp[i - 1][0] = inf;
for (int j = 1; j <= n; ++j) {
dp[i][j] = dp[i - 1][j];
for (int k = 1; k <= j; ++k) {
dp[i][j] = max(dp[i][j], min(dp[i - 1][j - k], p[i] / k));
}
}
}
int ans = dp[m][n];
if (ans == 0) {
puts("0 0");
continue;
}
memset(dp, 0x3f, sizeof dp);
for (int i = 1; i <= m; ++i) {
dp[i - 1][0] = 0;
for (int j = 1; j <= n; ++j) {
dp[i][j] = dp[i - 1][j];
for (int k = 1; k <= j; ++k) {
if (p[i] / k >= ans) {
dp[i][j] = min(dp[i][j], dp[i - 1][j - k] + p[i]);
}
}
}
}
printf("%d %d\n", ans, dp[m][n]);
}
return 0;
}

链接

传送门

题意

有三个高度相同的观测点共线,两两之间距离相等,给出观测点间的距离\(d\),三个观测点的高度\(h\),观测时的仰角\(\alpha\)\(\beta\)\(\gamma\),求火箭的高度。

思路

设火箭高于观测点高度为\(x\),火箭与三个观测点在平面上的投影分别为\(A\)\(B\)\(C\)\(D\)

易得

\[AB=\frac{x}{\tan{\alpha}},\space AD=\frac{x}{\tan{\beta}},\space AC=\frac{x}{\tan{\gamma}}, \space BC=2d\]

由中线定理得

\[AD=\frac{1}{2}\sqrt{2AB^2+2AC^2-BC^2}\]

带入得

\[4\left(\frac{x}{\tan{\beta}}\right)^2=2\left(\frac{x}{\tan{\alpha}}\right)^2+2\left(\frac{x}{\tan{\gamma}}\right)^2-4d^2\]

\[x^2\cdot\left(\frac{1}{\tan^2\alpha}+\frac{1}{\tan^2\gamma}-\frac{2}{\tan^2\beta}\right)=2d^2\]

解得

\[x=d\cdot\sqrt{\frac{2}{y}},\space y=\left(\frac{1}{\tan^2\alpha}+\frac{1}{\tan^2\gamma}-\frac{2}{\tan^2\beta}\right)\]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const double PI = acos(-1);

int main() {
double a, b, c, d, h;
scanf("%lf%lf", &d, &h);
while(~scanf("%lf%lf%lf", &a, &b, &c) && (a > 0 || b > 0 || c > 0)) {
a = tan(a * PI / 180), b = tan(b * PI / 180), c = tan(c * PI / 180);
printf("%.0f\n", d * sqrt(2 / (1 / a / a + 1 / c / c - 2 / b / b)) + h);
}
return 0;
}

链接

传送门

题意

给出一个数独,其中0可以填1到9任意数字,‘e’只能填偶数,‘o’只能填奇数,其余字母相同字母的格子的数字必须相同。输出给出的数独有多少个解。

思路

DLX基础题,注意限制。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>

using namespace std;

const int maxr = 750;
const int maxn = 330;
const int maxnode = 20000;

// 行编号从1开始,列编号为1~n,结点0是表头结点; 结点1~n是各列顶部的虚拟结点
struct DLX {
int n, sz; // 列数,结点总数
int S[maxn]; // 各列结点数

int row[maxnode], col[maxnode]; // 各结点行列编号
int L[maxnode], R[maxnode], U[maxnode], D[maxnode]; // 十字链表

int ansd, ans[maxr]; // 解

void init(int n) { // n是列数
this->n = n;
// 虚拟结点
for(int i = 0 ; i <= n; ++i) {
U[i] = i; D[i] = i; L[i] = i-1, R[i] = i+1;
}
R[n] = 0; L[0] = n;
sz = n + 1;
memset(S, 0, sizeof(S));
}

void addRow(int r, const vector<int>& columns) {
int first = sz;
for(int i = 0; i < columns.size(); ++i) {
int c = columns[i];
L[sz] = sz - 1; R[sz] = sz + 1; D[sz] = c; U[sz] = U[c];
D[U[c]] = sz; U[c] = sz;
row[sz] = r; col[sz] = c;
S[c]++; sz++;
}
R[sz - 1] = first; L[first] = sz - 1;
}

// 顺着链表A,遍历除s外的其他元素
#define FOR(i,A,s) for(int i = A[s]; i != s; i = A[i])

void remove(int c) {
L[R[c]] = L[c];
R[L[c]] = R[c];
FOR(i,D,c)
FOR(j,R,i) { U[D[j]] = U[j]; D[U[j]] = D[j]; --S[col[j]]; }
}

void restore(int c) {
FOR(i,U,c)
FOR(j,L,i) { ++S[col[j]]; U[D[j]] = j; D[U[j]] = j; }
L[R[c]] = c;
R[L[c]] = c;
}

int dfs(int d) {
if (R[0] == 0) {
ansd = d;
return 1;
}
int c = R[0], res = 0;
FOR(i,R,0) if(S[i] < S[c]) c = i;
remove(c);
FOR(i,D,c) {
ans[d] = row[i];
FOR(j,R,i) remove(col[j]);
res += dfs(d + 1);
FOR(j,L,i) restore(col[j]);
}
restore(c);
return res;
}

bool solve(vector<int>& v) {
v.clear();
if(!dfs(0)) return false;
for(int i = 0; i < ansd; i++) v.push_back(ans[i]);
return true;
}

} solver;

const int SLOT = 0;
const int ROW = 1;
const int COL = 2;
const int SUB = 3;

int encode(int a, int b, int c) {
return a * 81 + b * 9 + c + 1;
}

void decode(int code, int& a, int& b, int& c) {
--code;
c = code % 9; code /= 9;
b = code % 9; code /= 9;
a = code;
}

typedef pair<int, int> pii;

char s[15][15];
vector<pii> a[27];

int main() {
int t;
scanf("%d", &t);
while (t--) {
for (int i = 0; i < 9; ++i) {
scanf("%s", s[i]);
}
fill(a, a + 26, vector<pii>());
solver.init(324);
for (int r = 0; r < 9; ++r) {
for (int c = 0; c < 9; ++c) {
if (isdigit(s[r][c])) {
for (int v = 0; v < 9; ++v) {
if (s[r][c] == '0' || s[r][c] == v + '1') {
vector<int> columns;
columns.push_back(encode(SLOT, r, c));
columns.push_back(encode(ROW, r, v));
columns.push_back(encode(COL, c, v));
columns.push_back(encode(SUB, (r / 3) * 3 + c / 3, v));
solver.addRow(encode(r, c, v), columns);
}
}
} else if (s[r][c] == 'e' || s[r][c] == 'o') {
for (int v = s[r][c] == 'e' ? 1 : 0; v < 9; v += 2) {
vector<int> columns;
columns.push_back(encode(SLOT, r, c));
columns.push_back(encode(ROW, r, v));
columns.push_back(encode(COL, c, v));
columns.push_back(encode(SUB, (r / 3) * 3 + c / 3, v));
solver.addRow(encode(r, c, v), columns);
}
} else {
a[s[r][c] - 'a'].push_back(make_pair(r, c));
}
}
}
for (int i = 0; i < 26; ++i) {
if (a[i].empty()) {
continue;
}
int r = 0, c = 0;
for (int v = 0; v < 9; ++v) {
vector<int> columns;
for (int j = 0; j < a[i].size(); ++j) {
r = a[i][j].first, c = a[i][j].second;
columns.push_back(encode(SLOT, r, c));
columns.push_back(encode(ROW, r, v));
columns.push_back(encode(COL, c, v));
columns.push_back(encode(SUB, (r / 3) * 3 + c / 3, v));
}
solver.addRow(encode(r, c, v), columns);
}
}
printf("%d\n", solver.dfs(0));
}
return 0;
}

链接

传送门

题意

给出\(n \times m\)的矩阵,每个矩阵中的数字都在\([1,p]\),保证每个数字出现至少一次,\(p\)只出现1次。从\((1,1)\)出发,依次经过含有\(1\)\(p\),求最短路。

思路

\((i,j)\)包含的数字为\(x\),一个显然的状态\(dp[i][j]\)表示经过了前\(x\)个数之后到\((i,j)\)点的最短路,可以从所有包含\(x-1\)的格子转移过来。设包含\(x\)的格子数为\(cnt[x]\),每次转移的操作数为\(cnt[x]cnt[x-1]\)。但是直接这么做的复杂度最坏情况下达到了\(O(n^2m^2)\)并不能过。

所以我们加入暴力的方法,当\(cnt[x]cnt[x-1]>n*m\)时,直接用所有包含\(x-1\)的个子作为起点bfs,单次转移复杂度为\(O(nm)\)

可以证明,最终复杂度为\(O(nm\sqrt{nm})\)

复杂度证明:Codeforces Round #355 (Div. 2) Editorial

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <queue>
#include <map>
#define X first
#define Y second

using namespace std;

typedef pair<int, int> pii;

const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};
const int maxn = 305;

int d[maxn][maxn], dp[maxn][maxn];
queue<pii> q;
vector<pii> v[maxn * maxn];

int dis(const pii& a, const pii& b) {
return abs(a.X - b.X) + abs(a.Y - b.Y);
}

void Set(int& a, int b) {
if (a > b) {
a = b;
}
}

bool cmp(const pii& p1, const pii& p2) {
return dp[p1.X][p1.Y] < dp[p2.X][p2.Y];
}

int main() {
int n, m, p, x;
scanf("%d%d%d", &n, &m, &p);
memset(dp, 0x3f, sizeof dp);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &x);
v[x].push_back(make_pair(i, j));
if (x == 1) {
dp[i][j] = i + j - 2;
}
}
}
for (int i = 2; i <= p; ++i) {
if (v[i - 1].size() * v[i].size() <= n * m) {
for (pii& p1: v[i - 1]) {
for (pii& p2: v[i]) {
Set(dp[p2.X][p2.Y], dp[p1.X][p1.Y] + dis(p1, p2));
}
}
} else {
memset(d, -1, sizeof d);
for (pii& p: v[i - 1]) {
d[p.X][p.Y] = dp[p.X][p.Y];
}
sort(v[i - 1].begin(), v[i - 1].end(), cmp);
x = 1;
q.push(v[i - 1][0]);
while (!q.empty()) {
pii k = q.front();
q.pop();
while (x < v[i - 1].size() && dp[v[i - 1][x].X][v[i - 1][x].Y] <= d[k.X][k.Y]) {
q.push(v[i - 1][x++]);
}
for (int j = 0; j < 4; ++j) {
int a = k.X + dx[j], b = k.Y + dy[j];
if (a >= 1 && a <= n && b >= 1 && b <= m && d[a][b] == -1) {
d[a][b] = d[k.X][k.Y] + 1;
q.push(make_pair(a, b));
}
}
if (q.empty() && x < v[i - 1].size()) {
q.push(v[i - 1][x++]);
}
}
for (pii& p: v[i]) {
dp[p.X][p.Y] = d[p.X][p.Y];
}
}
}
printf("%d\n", dp[v[p][0].X][v[p][0].Y]);
return 0;
}

链接

传送门

题意

定义\(f(x)=Ax+B\)\(g^{(0)}(x)=x\)\(g^{(n)}(x)=g^{(n-1)}(x)\)。给出\(A\)\(B\)\(n\)\(x\) \((1 \leq A,B,x \leq 10^9,1 \leq n \leq 10^{18})\),求\(g^{(n)}(x)\)\(10^9+7\)取模的值。

思路

数据范围和题目形式很显然的就是矩阵题。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const LL mod = 1000000007;
const int maxn = 2;

struct Matrix {
LL a[maxn][maxn];

Matrix() {
memset(a, 0, sizeof a);
for (int i = 0; i < maxn; ++i) {
a[i][i] = 1;
}
}

Matrix operator * (const Matrix& rhs) const {
Matrix res;
memset(res.a, 0, sizeof res.a);
for (int k = 0; k < maxn; ++k) {
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
res.a[i][j] = (res.a[i][j] + a[i][k] * rhs.a[k][j]) % mod;
}
}
}
return res;
}
};

Matrix pow_mod(Matrix x, LL k) {
Matrix res, cur = x;
while (k) {
if (k & 1) {
res = res * cur;
}
cur = cur * cur;
k >>= 1;
}
return res;
}

int main() {
LL A, B, n, x;
scanf("%lld%lld%lld%lld", &A, &B, &n, &x);
Matrix ans;
ans.a[0][0] = A, ans.a[0][1] = B;
ans.a[1][0] = 0, ans.a[1][1] = 1;
ans = pow_mod(ans, n);
printf("%lld\n", (ans.a[0][0] * x + ans.a[0][1]) % mod);
return 0;
}

链接

传送门

题意

给出两个字符串\(s\)\(t\)和整数\(k\),在\(s\)\(t\)中找出出现顺序相同的\(k\)段相同连续子串,输出\(k\)段子串最大长度和。

思路

与最长公共子序列类似,定义状态\(dp[i][j][k][l]\)表示\(s\)匹配到了第\(i\)个字符、\(t\)匹配到了第\(j\)个字符,当前已经匹配了\(k\)段子串,\(l\)为0时代表最后一段子串停止匹配、为1时代表最后一段子串还在匹配。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int maxn = 1005;

int dp[maxn][maxn][11][2];
char s[maxn], t[maxn];

int main() {
int n, m, K;
scanf("%d%d%d", &n, &m, &K);
scanf("%s%s", s + 1, t + 1);
int ans = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = 1; k <= K; ++k) {
if (s[i] == t[j]) {
dp[i][j][k][1] = max(dp[i - 1][j - 1][k][1], dp[i - 1][j - 1][k - 1][0]) + 1;
}
dp[i][j][k][0] = max(dp[i][j][k][1], max(dp[i - 1][j][k][0], dp[i][j - 1][k][0]));
}
ans = max(ans, dp[i][j][K][0]);
}
}
printf("%d\n", ans);
return 0;
}

链接

传送门

题意

一年有\(2^n(1 \leq n \leq 10^{18})\)天,有\(k(2 \leq k \leq 10^{18})\)个人,求至少两人生日相同的概率。

思路

\(k > 2^n\)时,\(p=1\)

\(k \leq 2^n\)时,有\[p=1-\frac{(2^n - 1)(2^n - 2) \cdots (2^ n - k + 1)}{2^{(k - 1)n}}=\frac{b-a}{b}\]

可以在\(O(\log k)\)的时间内求出\(\gcd(a,b)\)

对于\(a\),若\(k-1 > mod\),则\(a=0\)。否则可以枚举求解。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <cstdio>

using namespace std;

typedef long long LL;

const LL mod = 1000003;

LL pow_mod(LL x, LL k) {
LL res = 1, cur = x;
while (k) {
if (k & 1) {
res = res * cur % mod;
}
cur = cur * cur % mod;
k >>= 1;
}
return res;
}

int main() {
LL n, k;
scanf("%lld%lld", &n, &k);
if (n < 63 && (1LL << n) < k) {
puts("1 1");
return 0;
}
LL tmp = 0, pow2_n = pow_mod(2, n);
for (LL i = 2; i < k; i <<= 1) {
tmp += (k - 1) / i;
}
LL gcd_inv = pow_mod(pow_mod(2, mod - 2), tmp);
LL a = 1, b = pow_mod(pow2_n, k - 1) * gcd_inv % mod;
if (k > mod) {
a = b;
} else {
for (int i = 1; i < k; ++i) {
a = a * (pow2_n - i + mod) % mod;
}
a *= gcd_inv;
a = ((b - a) % mod + mod) % mod;
}
printf("%lld %lld\n", a, b);
return 0;
}