2020省赛第八次训练赛题解
2020省賽第八次訓練賽題解
文章目錄
- 2020省賽第八次訓練賽題解
- 前面的碎碎念
- [A - Team Formation](https://vjudge.net/problem/ZOJ-3870)
- 思維題
- [B - Convex Hull](https://vjudge.net/problem/ZOJ-3871)
- 計算幾何,凸包
- [C - Beauty of Array](https://vjudge.net/problem/ZOJ-3872)
- dp
- 參考鏈接
- [D - Lunch Time](https://vjudge.net/problem/ZOJ-3875)
- 結構體的簡單排序
- [E - May Day Holiday](https://vjudge.net/problem/ZOJ-3876)
- 模擬題
- [F - Convert QWERTY to Dvorak](https://vjudge.net/problem/ZOJ-3878)
- 模擬題
- [G - Capture the Flag](https://vjudge.net/problem/ZOJ-3879)
- 模擬題
- [H - Demacia of the Ancients](https://vjudge.net/problem/ZOJ-3880)
- 水題
- [I - Ace of Aces](https://vjudge.net/problem/ZOJ-3869)
- 模擬題
- [J - Earthstone Keeper](https://vjudge.net/problem/ZOJ-3877)
- 最短路
前面的碎碎念
這場比賽的題目不是很難,簽到題占了快一半了,同樣的記錄本場比賽的原因是因為本場比賽的思維題占了一半,思維題可遇不可求,每一次遇到都大開眼界,吐槽一下hdu和fzu竟然在國慶期間不開放,導致前面幾場比賽沒機會寫題解,(其實也是沒時間,每天一場訓練賽對于我這種慢性子是需要很長很長很長的時間來補題的)你看看人家poj和zoj假期依然堅守崗位,在這里為兩大oj點贊,當然牛客和洛谷也是堅守崗位。沖沖沖!!
A - Team Formation
思維題
題目分析:題目的意思就是有一些人,兩兩組隊,每個人都有能力值,如果兩個人能力值的異或值大于每一個人單獨的能力值,那么這兩個人組的隊可以認為是一個出色的團隊。
首先這個題目暴力是不會出奇跡的,暴力是可以過樣例的但是顯然也會超時,那么我們可以從異或出發,兩個數的異或就是二進制的每一位異或,我們遵循相同為0,相異為1,也就是說如果兩個數對應位置上的數字不同那么結果就是1,回到題目中,題目要求的是異或兩個數之后變大,那么我們以其中一個數為基準,如果這個數二進制的某一位上為0,而對應另一個數相應位置并且這個位置是最高位二進制上為1,那么兩個數異或就會變大,不用去考慮其他位,因為就算其他位置異或變小了但是我們保證高位異或變大整體還是變大的。
對于任意的a(以下用二進制來表示)
如a
1011001
對應的b可以是
1* * * * *
1* *
1*
對于b
若b的最高位 對于 a來說 在a的當前位 為0 則符合上述條件
B - Convex Hull
計算幾何,凸包
題意:給n個點,|x[i]|,|y[i]| <= 1e9。求在所有情況下的子集下(子集點數>=3),凸包的面積和。
這題主要有幾個方面,一個是凸包的面積,可以直接用線段的有向面積和求得,這個當時沒想到。還有就是,在180度以內的點數。
所以實際上是枚舉2個點作為某個凸包的一條邊,看有多少個這樣的凸包。那個2^num - 1是保證除了2個點外至少還需1個點才能構成凸包。
時間復雜度O(n * n * logn)
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std;const double pi = acos(-1.0); #define ll long long #define maxn 1010 #define mod 998244353 ll area2(ll ax, ll ay, ll bx, ll by) {return ((ax * by % mod - ay * bx % mod) % mod + mod) % mod; } struct node {ll x, y;double ang;bool operator<(const node &b) const{return ang < b.ang;} } vec[maxn << 1]; ll p2[maxn]; int main() {p2[0] = 1;for (ll i = 1; i <= 1000; ++i)p2[i] = (p2[i - 1] << 1) % mod;ll t;scanf("%lld", &t);while (t--){ll n;scanf("%lld", &n);ll x[maxn], y[maxn];for (ll i = 0; i < n; ++i)scanf("%lld%lld", x + i, y + i);ll ans = 0;for (ll i = 0; i < n; ++i){for (ll j = 0; j < n; ++j){vec[j].x = x[j];vec[j].y = y[j];vec[j].ang = atan2(x[j] - x[i], y[j] - y[i]);}vec[i] = vec[n - 1];for (ll j = 0; j < n - 1; ++j){vec[j + n - 1] = vec[j];vec[j + n - 1].ang += pi * 2;}ll nn = n - 1 + n - 1;sort(vec, vec + nn);ll l = 0, r = 0;while (l < n - 1){while (r < nn && vec[r].ang - vec[l].ang < pi)r++;ll area = area2(x[i], y[i], vec[l].x, vec[l].y);ll cnt = (p2[r - l - 1] - 1 + mod) % mod;ans = (ans + 1ll * area * cnt % mod) % mod;++l;}}printf("%lld\n", (mod - ans) % mod);}return 0; }C - Beauty of Array
dp
參考鏈接
題目分析:這個題目很巧妙,題目要求的是所有連續序列中不同數字之和,出看可能不好寫,也不知道怎么去寫,暴力肯定會超時的,所以不用考慮。那么我們每添加一個元素進來,求當前序列包含a的所有序列之和,我們用兩個變量dp,sum,dp去儲存當前所有子序列的和,sum儲存整個序列的和,然后用一個變量去定義每個數字最終出現的位置。
說是這么說,當我第一次看題解的時候,我沒看懂,然后就自己手動模擬了一下代碼的過程,大致模擬完之后,也就知道代碼的意思了
#include <bits/stdc++.h> using namespace std; const int maxn = 100005; typedef long long ll; int a[maxn]; int main() {int t, m, x;scanf("%d", &t);while (t--){memset(a, 0, sizeof(a));scanf("%d", &m);ll ans = 0, dp = 0;for (int i = 1; i <= m; i++){scanf("%d", &x);dp = (i - a[x]) * x + dp;ans += dp;a[x] = i;//記住這個數的位置,如果下一個數和這個數相同的話,那么加上的就是下一個數字,就不會加上前面的和}printf("%lld\n", ans);}return 0; }D - Lunch Time
結構體的簡單排序
題目分析:題目的意思就是在每一樣菜品中選出中位數,如果一個菜有偶數個那么取最大的中位數
#include <bits/stdc++.h> using namespace std; const int maxn = 100005; typedef long long ll; struct node {string s;int num;bool operator<(const node x) const{return num < x.num;} } w[maxn]; int main() {int t;scanf("%d", &t);while (t--){int a, b, c;int ans = 0;string s = "";scanf("%d%d%d", &a, &b, &c);for (int i = 1; i <= a; ++i)cin >> w[i].s >> w[i].num;sort(w + 1, w + a + 1);ans += w[(a / 2) + 1].num;s += w[(a / 2) + 1].s;s += " ";for (int i = 1; i <= b; ++i)cin >> w[i].s >> w[i].num;sort(w + 1, w + b + 1);ans += w[(b / 2) + 1].num;s += w[(b / 2) + 1].s;s += " ";for (int i = 1; i <= c; ++i)cin >> w[i].s >> w[i].num;sort(w + 1, w + c + 1);ans += w[(c / 2) + 1].num;s += w[(c / 2) + 1].s;// s += " ";cout << ans << ' ' << s << endl;}return 0; }E - May Day Holiday
模擬題
https://blog.csdn.net/weixin_33700350/article/details/93304560
題目分析:這種題目之前遇見過,有點小惡心,需要我們手動計算一下(偷偷查看電腦日歷也是可以的)根據題意已知,2015年5月1日是星期五,據此推算。結果是1928年5月1日是星期二。用0-6分別表示星期天到星期六。,我們在計算的時候,只需要計算一下相差的年份,然后加一下,如果是閏年的話就多加一個1,然后對每一次的和都模一下7就行。
#include <iostream> #include <algorithm> #include <cstdio> #include <set> #include <map> #include <vector> #include <queue> #include <string.h> typedef long long ll; using namespace std; #define pi acos(-1.0) const int maxn = 1e5 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int ans[10] = {6, 9, 6, 5, 5, 5, 5};int judge(int x) {if (x % 4 == 0 && x % 100 != 0 || x % 400 == 0)return 1;return 0; } int main() {int t, y;scanf("%d", &t);while (t--){int d = 2, a = 1928;scanf("%d", &y);while (y != a){++a;d = (d + 365 + judge(a)) % 7;}printf("%d\n", ans[d]);} }F - Convert QWERTY to Dvorak
模擬題
題目分析:這是一個比較惡心的模擬題
#include <bits/stdc++.h> using namespace std; int main() {char a[128],x;for (int i = 0; i < 128; i++)a[i] = i;a['-'] = '[';a['='] = ']';a['['] = '/';a[']'] = '=';a['p'] = 'l';a['o'] = 'r';a['i'] = 'c';a['u'] = 'g';a['y'] = 'f';a['t'] = 'y';a['r'] = 'p';a['e'] = '.';a['w'] = ',';a['q'] = '\'';a['a'] = 'a';a['s'] = 'o';a['d'] = 'e';a['f'] = 'u';a['g'] = 'i';a['h'] = 'd';a['j'] = 'h';a['k'] = 't';a['l'] = 'n';a[';'] = 's';a['\''] = '-';a['.'] = 'v';a['/'] = 'z';a[','] = 'w';a['n'] = 'b';a['b'] = 'x';a['v'] = 'k';a['c'] = 'j';a['x'] = 'q';a['z'] = ';';a['Q'] = '"';a['W'] = '<';a['E'] = '>';a['{'] = '?';a['}'] = '+';a['_'] = '{';a['+'] = '}';a[':'] = 'S';a['"'] = '_';a['Z'] = ':';a['<'] = 'W';a['>'] = 'V';a['?'] = 'Z';while (~scanf("%c", &x)){if (x >= 'A' && x <= 'Z' && !(x == 'Q' || x == 'W' || x == 'E' || x == 'Z'))printf("%c", a[x + 32] - 32);elseprintf("%c", a[x]);}return 0; }G - Capture the Flag
模擬題
這是一個較為復雜的模擬題,復雜在題目意思不是很好的理解,代碼中也有很多細節地方需要處理
題目分析:首先輸入4個數,n,q,p,c
代表有n個隊伍,q個服務器,每支隊伍的初始分數p,還有c次操作
對于每次操作,首先輸入一個k,代表k次攻擊
每次攻擊有三個數,a,b,c,代表a通過c服務器成功的攻擊了b
對于每次成功的攻擊,b會失去n-1分,這n-1分會平分給通過同一服務器攻擊b的幾支隊伍
然后是q行,每行n個數,分別代表1~n是否成功維護了,1為成功,0為不成功
不成功的隊伍會失去n-1分,這失去的分會平分給成功維護的那些隊伍
然后輸入k個數,詢問這k支隊伍的分數
#include <bits/stdc++.h> using namespace std; #define exp 1e-5 const int inf = 0x3f3f3f3f;struct node {int id, rank;double score; } a[105];int n, q, c, t; double p; bool vis[105][105][15], hsh[105];int cmp1(node a, node b) {return a.score > b.score; }int cmp2(node a, node b) {return a.id < b.id; }int main() {scanf("%d", &t);while (t--){scanf("%d%d%lf%d", &n, &q, &p, &c);for (int i = 1; i <= n; ++i){a[i].id = i;a[i].score = p;}while (c--){int k;scanf("%d", &k);memset(vis, false, sizeof(vis));while (k--){int x, y, z;scanf("%d%d%d", &x, &y, &z);if (vis[x][y][z])continue;vis[x][y][z] = true;}for (int i = 1; i <= q; ++i) //服務器{for (int j = 1; j <= n; ++j) //防守方{int cnt = 0;for (int k = 1; k <= n; ++k) //攻擊方if (vis[k][j][i]) //統計攻擊j的隊伍有幾支cnt++;if (!cnt)continue;double ss = 1.0 * (n - 1) / cnt; //平分a[j].score -= (n - 1); //防守方失去n-1for (int k = 1; k <= n; ++k)if (vis[k][j][i]) //攻擊方得到分數a[k].score += ss;}}for (int i = 1; i <= q; ++i) //服務器{memset(hsh, false, sizeof(hsh));int cnt = 0;for (int j = 1; j <= n; ++j){int x;scanf("%d", &x);if (x) //成功維護的隊伍數{hsh[j] = true;cnt++;}else{hsh[j] = false;a[j].score -= (n - 1);}}if (cnt == n)continue;double ss = 1.0 * (n - 1) / cnt;ss = ss * (n - cnt);for (int j = 1; j <= n; ++j)if (hsh[j])a[j].score += ss;}sort(a + 1, a + n + 1, cmp1);for (int i = 1; i <= n; ++i) //更新排名{if (i != 1){if (fabs(a[i].score - a[i - 1].score) < exp)a[i].rank = a[i - 1].rank;elsea[i].rank = i;}elsea[i].rank = i;}sort(a + 1, a + n + 1, cmp2);scanf("%d", &k);while (k--){int x;scanf("%d", &x);printf("%f %d\n", a[x].score, a[x].rank);}}}return 0; }H - Demacia of the Ancients
水題
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int a[maxn]; int main() {int t;scanf("%d",&t);while(t--){int n;int ans=0;scanf("%d",&n);for(int i=1;i<=n;++i){scanf("%d",&a[i]);if(a[i]>6000)++ans;}printf("%d\n",ans);} }I - Ace of Aces
模擬題
題目分析:首先用一個map去儲存每一個數出現的次數,然后記錄維護一個最大值也就是出現最多的次數,之后我們就去遍歷map數組。如果有一個數出現的次數是最大次數,那么就符合題意,否則不滿足輸出Nobody
#include <iostream> #include <algorithm> #include <cstdio> #include <set> #include <map> #include <vector> #include <queue> #include <string.h> typedef long long ll; using namespace std; #define pi acos(-1.0) const int maxn = 1e5 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int a[maxn]; map<int, int> mp; map<int, int>::iterator it; int main() {int t;scanf("%d", &t);while (t--){mp.clear();int n, ans = 0;scanf("%d", &n);for (int i = 1; i <= n; ++i){scanf("%d", &a[i]);mp[a[i]]++;ans = max(ans, mp[a[i]]);}int cnt = 0, tot;for (it = mp.begin(); it != mp.end(); it++){if (it->second == ans){tot = it->first;++cnt;}}// cout << cnt << ' ' << 1 << endl;if (cnt == 1)printf("%d\n", tot);elseputs("Nobody");} }J - Earthstone Keeper
最短路
題目分析:這應該是目前我見過最難的最短路。所以鴿了鴿了
本題如果去掉怪物選項就是雙關鍵字最短路。
問題是現在多了一個怪物選項,因為這個怪物選項被殺死后他不會復生,因此我們不能重復計算這個值的答案。
所以對于每條過來的路,前面一個點遇到的怪物的后面的點就不用計算了,也就是去重。
根據這個思路,我們可以得到我們想要干的是當枚舉到當前點,我們希望計算出他的cost然后減去前一個點由怪物造成的cost。
所以怪物這個點是特殊的點,普通點和普通點就是按規則連邊,因為他們是相鄰的,去重比較容易。
我們多考慮一種連邊,因為題目告訴我們怪物是不可能相鄰的,這個信息顯然是有自己的道理,也給我們帶來啟發。
當我們枚舉到怪物點的時,我們將他上下左右這四個點分別連單向邊,時間為2,cost就是終點的cost-起點的cost
這樣就成功跳過了怪物類的點
參考鏈接
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pll; const int N = 2e6 + 10; const int mod = 1e9 + 7; int n, m; char g[550][550]; int sx, sy, ex, ey; int dx[] = {-1, 0, 1, 0}; int dy[] = {0, 1, 0, -1}; vector<pll> num; int h[N], ne[N], e[N], idx; int cost[N], w[N]; int st[N]; int dis[N][2]; struct node {int x, y;int id;bool operator<(const node &t) const{if (x == t.x)return y > t.y;return x > t.x;} }; void add(int a, int b, int c, int d) {e[idx] = b, ne[idx] = h[a], cost[idx] = c, w[idx] = d, h[a] = idx++; } bool check(int x, int y) {if (x >= 0 && x < n && y >= 0 && y < m){if (g[x][y] != '#')return true;}return false; } int solve(int x, int y) {int res = 0;if (g[x][y] >= 'a' && g[x][y] <= 'z')res += (g[x][y] - 'a' + 1);int i;for (i = 0; i < 4; i++){int a = x + dx[i];int b = y + dy[i];if (!check(a, b))continue;if (g[a][b] >= 'A' && g[a][b] <= 'Z')res += (g[a][b] - 'A' + 1);}return res; } int get(int a, int b, int c, int d) {int res = solve(c, d);int i, j;for (i = 0; i < 4; i++){int x = dx[i] + a;int y = dy[i] + b;if (check(x, y)){if (g[x][y] >= 'A' && g[x][y] <= 'Z'){for (int k = 0; k < 4; k++){int tmp1 = dx[k] + x;int tmp2 = dy[k] + y;if (check(tmp1, tmp2)){if (tmp1 == c && tmp2 == d){res -= (g[x][y] - 'A' + 1);}}}}}}return res; } void dij() {int ans1, ans2;priority_queue<node> q;q.push({0, 0, sx * m + sy});int i;for (i = 0; i <= n * m; i++){st[i] = 0;dis[i][1] = 0x3f3f3f3f;dis[i][0] = 0x3f3f3f3f;}dis[sx * m + sy][0] = dis[sx * m + sy][1] = 0;while (q.size()){auto t = q.top();q.pop();if (st[t.id])continue;st[t.id] = 1;if (t.id == ex * m + ey){ans1 = t.x;ans2 = t.y;break;}for (i = h[t.id]; i != -1; i = ne[i]){int j = e[i];if (dis[j][0] > dis[t.id][0] + cost[i]){dis[j][0] = dis[t.id][0] + cost[i];dis[j][1] = dis[t.id][1] + w[i];q.push({dis[j][0], dis[j][1], j});}else if (dis[j][0] == dis[t.id][0] + cost[i] && dis[j][1] > dis[t.id][1] + w[i]){dis[j][1] = dis[t.id][1] + w[i];q.push({dis[j][0], dis[j][1], j});}}}printf("%d %d\n", ans1, ans2); } int main() {//ios::sync_with_stdio(false);int t;cin >> t;while (t--){scanf("%d%d", &n, &m);int i, j;idx = 0;for (i = 0; i <= n * m; i++){h[i] = -1;}scanf("%d%d%d%d", &sx, &sy, &ex, &ey);sx--, sy--, ex--, ey--;for (i = 0; i < n; i++)scanf("%s", g[i]);for (i = 0; i < n; i++){for (j = 0; j < m; j++){int k;if (g[i][j] == '#')continue;if (g[i][j] >= 'A' && g[i][j] <= 'Z'){num.clear();for (k = 0; k < 4; k++){int x = i + dx[k];int y = j + dy[k];if (check(x, y)){num.push_back({x, y});}}for (k = 0; k < (int)num.size(); k++){for (int l = 0; l < (int)num.size(); l++){if (k == l)continue;int tmp1 = num[k].first * m + num[k].second;int tmp2 = num[l].first * m + num[l].second;add(tmp1, tmp2, get(num[k].first, num[k].second, num[l].first, num[l].second), 2); //計算到下個點的代價}}}else{for (int k = 0; k < 4; k++){int x = i + dx[k];int y = j + dy[k];if (!check(x, y))continue;if (g[x][y] >= 'A' && g[x][y] <= 'Z')continue;add(i * m + j, x * m + y, solve(x, y), 1);}}}}dij();}return 0; }2020.10.12湖人在總決賽以比分4:2一舉擊敗熱火,奪得了今年的總冠軍,is for kobe ,is also for lakers,叫一個賽季的湖人總冠軍,終于實現了!!!千言萬語,終究化為“湖人總冠軍!!!”
總結
以上是生活随笔為你收集整理的2020省赛第八次训练赛题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [递归]一文看懂递归
- 下一篇: 中秋望月