前往独立博客获取更好阅读体验:http://kesisour.work/index.php/archives/90/
题源: https://ac.nowcoder.com/acm/contest/9925
G Fibonacci
签到题,斐波那契数列中偶数的个数为长度的三分之一,贡献值从 n 向下递减
/// ~~AK~~ Cu
#include <bits/stdc++.h>
#define int ll
typedef long long ll;
using namespace std;
int n;
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
int p = n / 3;
int sum = (2 * n - p - 1) * p / 2;
cout << sum << endl;
return 0;
}
M Gitignore
树上DFS,根据文件目录建树,不能被忽略的节点向上回溯至根节点的所有节点全都不能被忽略,标记完 tag 后从根节点搜索,遇到可以忽略的就返回,否则继续深搜,统计需要忽略的节点个数。
/// ~~AK~~ Cu
#include <bits/stdc++.h>
#define int ll
typedef long long ll;
using namespace std;
const int M = 100005;
int t, n, m;
map<string, int> s;
string st, newst;
int oldcnt = 0, newcnt = 0;
int flag[100005] = {0};
int C = 0, head[100005];
struct edge {
int to, nxt;
} edge[M];
inline void add_adge(int u, int v) {
edge[C].to = v;
edge[C].nxt = head[u];
head[u] = C;
C++;
}
int dfs(int x) {
if (flag[x]) {
int tmp = 0;
for (int i = head[x]; i != -1; i = edge[i].nxt) {
tmp += dfs(edge[i].to);
}
return tmp;
} else return 1;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
for (int ii = 0; ii < t; ii++) {
int cnt = 0;
C = 0;
memset(head, -1, sizeof(head));
memset(flag, 0, sizeof(flag));
flag[0] = 1;
oldcnt = newcnt = 0;
s.clear();
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> st;
st = st + '/';
oldcnt = newcnt = 0;
for (int j = 0; j < st.length(); j++) {
if (st[j] == '/') {
oldcnt = newcnt;
newst = st.substr(0, j);
if (!s.count(newst)) {
s[newst] = ++cnt;
newcnt = s[newst];
add_adge(oldcnt, newcnt);
}
newcnt = s[newst];
}
}
}
for (int i = 0; i < m; i++) {
cin >> st;
st = st + '/';
for (int j = st.length() - 1; j > 0; j--) {
if (st[j] == '/') {
newst = st.substr(0, j);
if (!s.count(newst)) {
continue;
}
newcnt = s[newst];
flag[newcnt] = 1;
}
}
}
int ans = dfs(0);
cout << ans << endl;
}
return 0;
}
B Mine Sweeper II
思维题,考虑数字和就等于相邻(雷格子,非雷格子)二元组的个数,于是把整个地图全反过来这个二元组个数不变,然后 B 与 A 之间以及 B 与 inv(A) ,即把 A 所有格子全部取反的扫雷地图之间总有一个偏差不超过一半的,所以就选其中偏差不超过一半的然后变过去就行。
/// ~~AK~~ Cu
#include <bits/stdc++.h>
#define int ll
typedef long long ll;
using namespace std;
int n, m;
string a[1005], b[1005];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int ans = 0;
for (int i = 0; i < n; i++)
cin >> b[i];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] != b[i][j]) ans++;
}
}
if (ans <= (n * m) / 2) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cout << a[i][j];
}
cout << endl;
}
} else {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] == '.')a[i][j] = 'X';
else a[i][j] = '.';
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cout << a[i][j];
}
cout << endl;
}
}
return 0;
}
D Walker
分类讨论两个点的贡献情况:
- 一个点走完所有。
- 两个点对着向对方的边界走。
- 两个点共同走中间的部分。中间的界限在两边同时贡献完的情况下总时间最短,因此可以二分查找。
赛场AK代码分类的有点复杂,不过也能正确通过。
/// ~~AK~~ Cu
#include <bits/stdc++.h>
#define int ll
typedef long long ll;
using namespace std;
int t;
double n, p1, p2, v1, v2, tmp;
double fun(double l, double r, double p, double v) {
double l1 = p - l, l2 = r - p;
return (min(l1, l2) * 2 + max(l1, l2)) / v;
}
double mid, t1, t2;
double erfen(double l, double r) {
mid = (l + r) / 2;
t1 = fun(0, mid, p1, v1);
t2 = fun(mid, n, p2, v2);
if (fabs(t1 - t2) <= 1e-6) {
return t1;
} else if (t1 < t2) {
return erfen(mid, r);
} else {
return erfen(l, mid);
}
}
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d", &t);
for (int i = 0; i < t; i++) {
//cin >> n >> p1 >> v1 >> p2 >> v2;
scanf("%lf %lf %lf %lf %lf", &n, &p1, &v1, &p2, &v2);
double ans = 0.0;
if (p1 - p2 >= 0.00000001) {
tmp = p1;
p1 = p2;
p2 = tmp;
tmp = v1;
v1 = v2;
v2 = tmp;
}
if (v1 - v2 >= 0.00000001) {
tmp = p1;
p1 = n - p2;
p2 = n - tmp;
tmp = v1;
v1 = v2;
v2 = tmp;
}
if (p1 == p2) {
ans = max(p1 / v1, (n - p2) / v2);
}
else if ((n - p2) / v2 - fun(0, p2, p1, v1) >= 0.00000001) {
ans = (n - p2) / v2;
}
else if (p1 / v1 - fun(0, n, p2, v2) >= 0.00000001) {
ans = fun(0, n, p2, v2);
} else if (p1 / v1 - fun(p1, n, p2, v2) >= 0.00000001) {
ans = p1 / v1;
} else {
/// 二分 x
ans = erfen(p1, p2);
}
double ans2 = max((n - p1) / v1, p2 / v2);
ans = min(ans, ans2);
printf("%.10lf\n", ans);
}
return 0;
}
C Sum of Log
根据性质可以得出 log2(i + j) == log2(max(i, j)),即最大为1的二进制位不会改变,考虑按二进制位进行数位DP。
dp[i][j][k][l]
表示当前第i位,j&k 都是 0/1 变量,表示当前状态是否抵住了X&Y的上界,l表示当前状态的最高有效位。
可能实现方法略微复杂,甚至在交题的时候有一定概率会T…,抽空补一下递推的数位DP版本。
#include <bits/stdc++.h>
#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define int ll
typedef long long ll;
using namespace std;
template<typename T>
void read(T &res) {
bool flag = false;
char ch;
while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
flag && (res = -res);
}
template<typename T>
void write(T x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
///
const int mod = 1e9 + 7;
int t, x, y, tmp, lx, ly;
int digx[35], digy[35];
int dp[35][2][2][35];
int dfs(int dig, int limx, int limy, int maxdig) {
if (!dig) return maxdig;
if (dp[dig][limx][limy][maxdig]) return dp[dig][limx][limy][maxdig];
int ans = 0;
if ((limx and digx[dig]) or !limx) ans += dfs(dig - 1, limx, limy and (!digy[dig]), (maxdig ? maxdig : dig));
if ((limy and digy[dig]) or !limy) ans += dfs(dig - 1, limx and (!digx[dig]), limy, (maxdig ? maxdig : dig));
ans += dfs(dig - 1, limx and (!digx[dig]), limy and (!digy[dig]), maxdig);
dp[dig][limx][limy][maxdig] = ans % mod;
return dp[dig][limx][limy][maxdig];
}
signed main() {
read(t);
while (t--) {
memset(dp, 0, sizeof(dp));
memset(digx, 0, sizeof(digx));
memset(digy, 0, sizeof(digy));
read(x), read(y);
tmp = x, lx = 0, ly = 0;
while (tmp) {
digx[++lx] = tmp % 2;
tmp = tmp >> 1;
}
tmp = y;
while (tmp) {
digy[++ly] = tmp % 2;
tmp = tmp >> 1;
}
write(dfs(max(lx, ly), 1, 1, 0)), pn;
}
return 0;
}
I Sky Garden
思维题,挺简单的,注意 m 为 1 的时候圆心是没有端点的。
#include <bits/stdc++.h>
#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define int ll
typedef long long ll;
using namespace std;
template<typename T>
void read(T &res) {
bool flag = false;
char ch;
while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
flag && (res = -res);
}
template<typename T>
void write(T x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
///
const double pi = 3.1415926535;
int n, m;
double ans;
signed main() {
read(n), read(m);
if (m != 1) ans = n * m * (n + 1);
for (int i = 1; i <= n; i++) {
double tmp = 0;
for (int j = 1; j <= m; j++) {
tmp += min((double)2 * i, pi * i * j / m);
}
tmp *= 2.0 * m;
tmp -= 2.0 * m * i;
ans += tmp;
}
for (int i = 1; i < n; i++) {
double tmp = 0;
for (int j = 1; j <= m; j++) {
tmp += min((double)2 * i, pi * i * j / m);
}
tmp *= 2.0;
tmp -= 2.0 * i;
tmp *= 2.0 * m;
tmp *= n - i;
ans += tmp;
ans += (double) m * (n - i + 1) * (n - i) * 2 * m;
}
printf("%.8f", ans);
return 0;
}
L Traveling in the Grid World
思维题
若 gcd(m - a, n - b) == 1,则 (m, n) 能否从点 (a, b) 直接到达。
第一个结论:不能直接到达的点最多有一个转折点就可以到达。
证明:gcd(1, n - 1) == 1 and gcd(m - 1, 1) == 1一定成立。
第二个结论:若某转移点使得总距离最小,则途中两条路一定不会经过点。
证明:若从 A 到 B 经过转移点 C,此时有点 D 位于 AC 路径中,那么 AD + BD < AC + BC
第三个结论:最优解一定只需要一个转移点。
证明:若从 A 到 B 经过两个转移点 C 和 D,那么总路径为 AC + CD + BD,此时BC > BD + CD。若 BC 可以直接到达,则不需要点 D 答案更优,若 BC 不能直接到达,则参见第二结论, BC 中一定存在点 E 使得 AE + BE < AC + BC,答案更优。
综上,若不能直接到达,暴力直线附近的点作为转移点即可。
#include <bits/stdc++.h>
#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define int ll
typedef long long ll;
using namespace std;
template<typename T>
void read(T &res) {
bool flag = false;
char ch;
while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
flag && (res = -res);
}
template<typename T>
void write(T x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
///
int t, n, m;
int f, c;
signed main() {
read(t);
while (t--) {
read(n), read(m);
if (__gcd(m, n) == 1) {
printf("%.9lf\n", sqrt(m * m + n * n));
continue;
}
double k = (double) m / n;
double ans = 200000005.0;
f = 1, c = 0;
while (f) {
for (int i = 1; i <= n; i++) {
int y = k * i - c;
if (fabs((double)y / i - k) < 1e-10) continue;
if (__gcd(i, y) == 1 and __gcd(n - i, m - y) == 1) {
f = 0;
ans = min(ans, sqrt(y * y + i * i) + sqrt((n - i) * (n - i) + (m - y) * (m - y)));
}
}
c++;
}
printf("%.9lf\n", ans);
}
return 0;
}