1 比赛
1.1 那23个路口
题意
给定一个$m $*$n$的权值图,求走23步后最大的可取的权值和。
分析
我们可以通过dp来解决此题。$dp[i][j][k]$表示在第$i$行$j$列走了$k$次最大的权值和。
那么可以轻易地得到$dp[i][j][k] = dp[i -1][j][k-1]+dp[i+1][j][k-1]+dp[i][j+1][k-1]+dp[i][j-1][k-1]$
#include <iostream>
using namespace std;
const int N = 500 + 5;
const int fx[4] = {0, 0, -1, 1};
const int fy[4] = {-1, 1, 0, 0};
typedef long long ll;
int a[N][N];
ll f[N][N][25];
int n, m, sx, sy;
bool check_where(int x, int y)
{
return x < 1 ? false : x > n ? false : y < 1 ? false : y <= m;
}
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i ++ )
{
f[i][0][0] = -100000;
f[i][m + 1][0] = -100000;
for (int j = 1; j <= m; j ++ )
{
f[0][j][0] = -100000;
f[n + 1][j][0] = -100000;
cin >> a[i][j];
if (!a[i][j])
{
sx = i, sy = j;
}
f[i][j][0] = -100000;
}
}
f[sx][sy][0] = 0;
for (int k = 1; k <= 23; k ++ )
{
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
long long t = -100000000;
for (int e = 0; e < 4; e ++ )
{
int nx = i + fx[e];
int ny = j + fy[e];
if (!check_where(nx, ny))
{
continue;
}
t = max(t, f[nx][ny][k - 1]);
}
if (t <= -1 || a[i][j] == -1 || a[i][j] == 0)
{
f[i][j][k] = -100000;
continue;
}
f[i][j][k] = t + a[i][j];
}
}
}
ll res = 0;
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
res = max(res, f[i][j][23]);
}
}
cout << res;
}
总结
认 真 读 题
1.2 我们的可可西里
题面
转眼到了2008年的6月9日,盼望已久的高考结束了。我们踏上了向西的旅程(本来是想写西去之路,可是考虑不太妥当)。可可西里,多么诱人的名词,充满了奇幻的色彩和自然的淳朴。从可可西里徒步走回家的决定是在1年半前定下的,而现在,终于可以实现那个钩过手指的预定。我们的可可西里。。。
在回家的路上,疯子和蚊子看到了许多可爱的藏羚羊,无意之中疯子和蚊子发现藏羚羊的居住地的分布也是有规律的,虽然疯子和蚊子早就听说藏羚羊是一种群体性很强又有超高IQ的动物,但是还是为它们的居住地分布规律感到惊叹。经过细心的观察,疯子和蚊子发现,如果假设一个藏羚羊群体有N只羊,就可以把它们的领地当做一个N*N的方阵,在这个方阵上第I列的第I 行都有一个圣地,它们不会居住在圣地,同时每行每列只能居住一只羚羊。于是他们很快算出一个有N只羊的藏羚羊群体的居住地分布方法数。
这是圣地的一种排列方法
思路
错排+高精。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 2e3 + 5;
string f[N];
char c[10000], t[10000];
long long r[1000000];
string add(string a, string b)
{
strcpy(c, a.c_str());
strcpy(t, b.c_str());
int lena = a.size(), lenb = b.size();
for (int i = 0; i < lena; i ++ )
c[i] -= '0';
for (int i = 0; i < lenb; i ++ )
t[i] -= '0';
reverse(c, c + lena);
reverse(t, t + lenb);
int len = max(lena, lenb), jw = 0;
for (int i = 0; i < len; i ++ )
{
r[i] = c[i] + t[i] + jw;
jw = 0;
if (r[i] >= 10)
{
r[i] -= 10;
jw = 1;
}
}
if (jw)
{
r[len] = 1;
len ++;
}
string res = "";
for (int i = len - 1; i >= 0; i -- )
{
res += char(r[i] + '0');
}
return res;
}
string mul(string a, string b)
{
memset(r, 0, sizeof(r));
strcpy(c, a.c_str());
strcpy(t, b.c_str());
int lena = a.size(), lenb = b.size();
for (int i = 0; i < lena; i ++ )
c[i] -= '0';
for (int i = 0; i < lenb; i ++ )
t[i] -= '0';
reverse(c, c + lena);
reverse(t, t + lenb);
int len = max(lena, lenb), jw = 0;
for (int i = 0; i < lena; i ++ )
{
for (int j = 0; j < lenb; j ++ )
{
r[i + j] += c[i] * t[j];
}
}
for (int i = 0; i < lena + lenb; i ++ )
{
r[i] += jw;
jw = r[i] / 10;
r[i] %= 10;
}
if (jw)
{
r[lena + lenb] = jw;
}
lena ++;
string res = "";
bool flag = false;
for (int i = lena + lenb - 1; i >= 0; i -- )
{
if (r[i] > 0)
flag = true;
if (flag == true)
res += char(r[i] + '0');
}
return res;
}
string tostring(int x)
{
string res = "";
while (x >= 10)
{
res = char(x % 10 + '0') + res;
x /= 10;
}
res = char(x % 10 + '0') + res;
return res;
}
int main()
{
int n;
cin >> n;
f[2] = "1";
for (int i = 3; i <= n; i ++ )
{
f[i] = mul(tostring(i - 1), add(f[i - 1], f[i - 2]));
//cout << tostring(i - 1) << "-" << add(f[i - 1], f[i - 2]) << endl;
//cout << endl;
//cout << f[i] << endl;
//cout << endl;
//cout << endl;
}
cout << f[n];
return 0;
}
//dn=(n-1)*(dn-1 + dn-2)
总结
先写错排再写高精是好方法。
[heimu]这题高精太**难写了[/heimu]
[heimu]剩下两题摸了[/heimu]
2.L C A
2.1 P2937 [USACO09JAN]Laserphones S
[heimu]BFS好题[/heimu]
以上。
2.2 P3398 仓鼠找sugar
思路
我们可以假设若存在$a, b$与$c, d$路径相交。[heimu]不妨[/heimu]设$x=lca(a, b)$以及$y=lca(c, d)$。
那么因为$a, b$与$c, d$路径相交,则$x$在$c,d$上或$y$在$a,b$上。
好,我们再来设定$z$在$a,b$上,并且$z$与$c,d$相交。那么可以确认的就是$x,y$为$z$的祖先(不管多上面)
那么$x, y$的那个更深的那个,一定在另一个路径上。
那么问题就改成了给定一个点,确认这个点是否在另一条边上。(在此题中即判断$x是否在c,d边上,y是否在a,b边上。$)
判断方法为:
$$ dis_{x,a} +dis_{x,b}=dis_{a,b} \\dis_{a,b}=deep_a+deep_b-2*deep_{(lca(a,b))} $$
然后就可以解决这道题了awa
// Problem: P3398 仓鼠找sugar
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3398
// Memory Limit: 125 MB
// Time Limit: 1000 ms
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
int n, q;
vector<int> g[N];
int d[N], f[N][20];
void dfs(int x, int y)
{
d[x] = d[y] + 1;
f[x][0] = y;
int len = g[x].size();
for (int i = 0; i < len; i ++ )
{
if (g[x][i] != y)
{
dfs(g[x][i], x);
}
}
}
int lca(int x, int y)
{
if (d[x] < d[y])
{
swap(x, y);
}
for (int i = 19; i >= 0; i -- )
{
if (d[f[x][i]] >= d[y])
{
x = f[x][i];
}
}
if (x == y)
{
return x;
}
for (int i = 19; i >= 0; i -- )
{
if (f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int dis(int x, int y)
{
return d[x] + d[y] - 2 * d[lca(x, y)];
}
int main()
{
cin >> n >> q;
for (int i = 1; i < n; i ++ )
{
int l, r;
cin >> l >> r;
g[l].push_back(r);
g[r].push_back(l);
}
dfs(1, 0);
for (int i = 1; i <= 19; i ++ )
for (int j = 1; j <= n; j ++ )
f[j][i] = f[f[j][i - 1]][i - 1];
while (q --)
{
int ca, cb, cc, cd;
cin >> ca >> cb >> cc >> cd;
int x = lca(ca, cb);
int y = lca(cc, cd);
if (d[x] > d[y])
{ // x on c, d
cout << ((dis(x, cc) + dis(x, cd) == dis(cc, cd)) ? 'Y' : 'N') << endl;
}
else
{ // y on a, b
cout << ((dis(y, ca) + dis(y, cb) == dis(ca, cb)) ? 'Y' : 'N') << endl;
}
}
return 0;
}