[補題] 第 45 屆國際大學生程式設計競賽(ICPC)亞洲區域賽(上海)
前往獨立部落格獲取更好閱讀體驗: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;
}
相關文章
- 第43屆ACM-ICPC國際大學生程式設計競賽 亞洲區域賽南京站現場賽名額分配相關說明ACM程式設計
- 2023 國際大學生程式設計競賽亞洲區域賽(濟南站)(SMU Autumn 2024 Team Round 2)程式設計
- 第15屆浙江省大學生程式設計競賽D題程式設計
- [題解][2021-2022年度國際大學生程式設計競賽第10屆陝西省程式設計競賽] Type The Strings程式設計
- 第 43 屆 ACM-ICPC 亞洲區域賽(徐州)現場賽名額分配規則及相關說明ACM
- 第 10 屆 CCPC 中國大學生程式設計競賽濟南站 遊記程式設計
- 第十屆山東省大學生程式設計競賽題解(A、F、M、C)程式設計
- 2020 ICPC 上海賽區
- 紹興市大學生程式設計競賽程式設計
- 第八屆山東省ACM大學生程式設計競賽總結ACM程式設計
- 山東省第七屆ACM大學生程式設計競賽-Reversed WordsACM程式設計
- 牛客競賽,GDDU第十屆文遠知行杯新生程式設計競賽,摸魚記(BDEIKL題解,補G,ACFHJ)程式設計
- 2020“數維杯”國際大學生數學建模競賽賽題分析
- 山東省第一屆ACM大學生程式設計競賽-Balloons(搜尋)ACM程式設計
- 湖南省大學生程式設計競賽系統設計程式設計
- 2012年"浪潮杯"山東省第三屆ACM大學生程式設計競賽(熱身賽)ACM程式設計
- ACM-ICPC世界冠軍教你如何備戰程式設計競賽ACM程式設計
- 山東省第四屆ACM大學生程式設計競賽-Rescue The Princess(計算幾何)ACM程式設計
- 第十五屆全國大學生智慧車安徽賽區參賽須知和競賽日程安排
- 山東省第六屆ACM大學生程式設計競賽-Square Number(完全平方數)ACM程式設計
- 山東省第六屆ACM大學生程式設計競賽-Lowest Unique Price(桶排序)ACM程式設計排序
- 第二十屆西南科技大學ACM程式設計競賽(同步賽)ACM程式設計
- 第十四屆全國大學生資訊保安競賽——創新實踐能力賽(東北賽區)比賽圓滿落幕
- 無錫學院2024年ACM大學生程式設計競賽校選賽 題解ACM程式設計
- 華中農業大學第十三屆程式設計競賽 題解程式設計
- QZEZ第一屆“飯吉圓”杯程式設計競賽程式設計
- 第十四屆全國大學生資訊保安競賽創新實踐能力賽(華中賽區)比賽成功舉辦
- 山東省第四屆ACM大學生程式設計競賽-Contest Print Server(模擬)ACM程式設計Server
- 北京資訊科技大學第十一屆程式設計競賽(重現賽)I程式設計
- 某大學程式設計競賽程式設計
- 2020年“感恩杯”台州學院第十三屆大學生程式設計競賽D、H、I題解(後續補充)程式設計
- 華中農業大學第十三屆程式設計競賽程式設計
- 山東省第六屆ACM大學生程式設計競賽-Single Round Math(大數除法)ACM程式設計
- 2024國慶鋁紫程式設計競賽程式設計
- 筆記:《挑戰程式設計競賽(第2版)》(2)筆記程式設計
- 筆記:《挑戰程式設計競賽(第2版)》(3)筆記程式設計
- 筆記:《挑戰程式設計競賽(第2版)》(1)筆記程式設計
- 第二屆“演算法控”馬拉松程式設計競賽 解題思路演算法程式設計