Codeforces Round #693 (Div. 3)A~G解题报告
Codeforces Round #693 (Div. 3)A~G解題報告
A Cards for Friends
原題信息
http://codeforces.com/contest/1472/problem/A
解題思路
本題就是一個找 x/2i=old,y/2j=oldx/2^i=old,y/2^j=oldx/2i=old,y/2j=old, 返回 2i?2j>=n2^i*2^j>=n2i?2j>=n
一般這樣的題目都需要注意使用 LL
AC代碼
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <fstream> using namespace std;typedef long long LL;bool inline Check(LL x, LL y, LL n) {LL ret = 1;while ((x & 1) == 0){ret <<= 1;x >>= 1;if (ret >= n)return true;}while ((y & 1) == 0){y >>= 1;ret <<= 1;if (ret >= n)return true;}if (ret >= n) return true;else return false; }int main() {int T; cin >> T;while (T -- ){static LL x, y, n;cin >> x >> y >> n;if (Check(x, y, n))cout << "YES\n";elsecout << "NO\n";}return 0; }B Fair Division
原題信息
http://codeforces.com/contest/1472/problem/B
解題思路
能否將糖果平均分,直接分類討論
首先統(tǒng)計 1gram1gram1gram 數量為cnt1cnt1cnt1,2gram2gram2gram數量為cnt2cnt2cnt2下面對cnt1,cnt2cnt1,cnt2cnt1,cnt2進行分類討論
cnt1偶數,cnt2偶數cnt1偶數,cnt2偶數cnt1偶數,cnt2偶數
————可以直接進行平均分
cnt1偶數,cnt2奇數cnt1偶數,cnt2奇數cnt1偶數,cnt2奇數
————當cnt1=0cnt1=0cnt1=0時,不可以平均分
————當cnt1!=0cnt1!=0cnt1!=0時,可以平均分,只需要將cnt1?=2,cnt2++cnt1-=2,cnt2++cnt1?=2,cnt2++即可
cnt1奇數,cnt2偶數或偶數cnt1奇數,cnt2偶數或偶數cnt1奇數,cnt2偶數或偶數
————因為cnt1cnt1cnt1為奇數,導致總的grams根本就是個奇數,是不可能平均分的。
AC代碼
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <fstream> using namespace std;typedef long long LL;const int N = 200010; int a[N], n;bool inline Check() {static int cnt1, cnt2, sum;cnt1 = cnt2 = sum = 0;for (int i = 1; i <= n; i ++ ){sum += a[i];cnt1 += (a[i] == 1);cnt2 += (a[i] == 2);}if (sum & 1) return false;else if (cnt1 == 0 && cnt2 % 2 == 1) return false;else return true; }int main() {int T; cin >> T;while (T -- ){cin >> n;for (int i = 1; i <= n; i ++ ){scanf("%d", &a[i]);}if (Check())cout << "YES\n";elsecout << "NO\n";}return 0; }C Long Jumps
原題信息
http://codeforces.com/contest/1472/problem/C
解題思路
一個較為簡單的dp,需要注意的是數組下標的特判,防止越界,最好開LL
fif_ifi?表示選擇iii做為StartPositionStartPositionStartPosition的游戲結果
那么,我們可以得到遞歸關系式為
————fi=ai+fi+aif_i=a_i+f_{i+a_i}fi?=ai?+fi+ai?? 條件為當 i+ai<=ni+a_i<=ni+ai?<=n
————否則fi=aif_i=a_ifi?=ai?
上述的這個討論就是為了避免數組的越界
AC代碼
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <fstream> using namespace std;typedef long long LL;const int N = 200010; int a[N], n; LL res = 0; LL f[N];int main() {int T; cin >> T;while (T -- ){cin >> n;for (int i = 1; i <= n; i ++ ){scanf("%d", &a[i]);}memset(f, 0, sizeof f);res = 0LL;for (int i = n; i >= 1; i -- ){f[i] = a[i] + (i + a[i] <= n ? f[i + a[i]] : 0);res = max(res, f[i]);}/// printf("###########\n");cout << res << endl;}return 0; }D Even-Odd Game
原題信息
http://codeforces.com/contest/1472/problem/D
解題思路
題目大意是給定一個數組,AliceBobAlice BobAliceBob輪流在里面選一個數, AliceAliceAlice選擇偶數,那么加和偶數一樣大的分, 選擇奇數不加分;BobBobBob選擇奇數,那么加和奇數一樣大的分,選擇偶數不加分。
從題意出發(fā),我們不難想到下面這種求解方法,將奇數、偶數分開,兩個數組分別進行非遞增排序,每次選擇的時候從兩個數組選取最大的,進行拿出。(好像不分開也行)
對于AliceAliceAlice來講,選取當前最大的數是偶數,那么加分最多,選取最大的數是奇數,是為了防止BobBobBob加分過多!同理,對于BobBobBob也是如此,下面我們來證明他的合法性。
- 直接嚴格證明是很難的,我們不妨輔助的思想來看這個問題,對于人物A來說,他可以選擇自己可以加分的數字,也可以選擇不讓別人加分的數(自己不加分),不讓別人加分,就類似于讓自己加分(因為讓別人加分少了)
- 也就是說讓自己的得分減去另一個人得分更大,自己的贏面更大。因此對于博弈雙方,我們只需要選擇當前最大數即可。
- 記得開LL
AC代碼
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <fstream> using namespace std;typedef long long LL;const int N = 200010; int a[N], n; int even_a[N], old_a[N], idx_e, idx_o; LL score_a, score_b; int cur_o = 1, cur_e = 1; bool cmp(int a, int b){ return a > b; }bool Check() {if (cur_o > idx_o) return false;if (cur_e > idx_e) return true;if (old_a[cur_o] >= even_a[cur_e]) return true;else return false; } int main() {int T; cin >> T;while (T -- ){cin >> n;score_a = score_b = 0LL;idx_e = idx_o = 0;for (int i = 1; i <= n; i ++ ){scanf("%d", &a[i]);if (a[i] & 1) old_a[++ idx_o] = a[i];else even_a[++ idx_e] = a[i];}sort(even_a + 1, even_a + idx_e + 1, cmp);sort(old_a + 1, old_a + idx_o + 1, cmp);cur_o = 1, cur_e = 1;for (int i = 1; i <= n; i ++ ){if (Check()) // 奇數{if (i & 1){cur_o ++;}else{score_b += old_a[cur_o ++];}}else // 偶數{if (i & 1){score_a += even_a[cur_e ++];}else{cur_e ++;}}}if (score_a == score_b) puts("Tie");else if (score_a > score_b) puts("Alice");else puts("Bob");}return 0; }E Correct Placement
原題信息
解題思路
對于每一個(h,w)(h, w)(h,w)我們需要找到(hi,wi),hi<h,wi<w(h_i, w_i), h_i<h,w_i<w(hi?,wi?),hi?<h,wi?<w的下標,至于另一種情況我們直接 swap(h,w)swap(h, w)swap(h,w)即可,
主要是先對(w,h)(w, h)(w,h)進行排序二分查詢 hi<hh_i<hhi?<h的最大下標,找他的前綴最小值www(可以預處理)
AC代碼
#include <bits/stdc++.h> using namespace std;const int N = 200010; const int INF = 0x3f3f3f3f; class Node { public:int h, w;int idx;void operator=(const Node &t1){h = t1.h, w = t1.w, idx = t1.idx;} }a[N], q[N]; int pre_w[N], pre_idx[N]; int n;bool cmp(const Node &t1, const Node &t2) {if (t1.h == t2.h)return t1.w < t2.w;elsereturn t1.h < t2.h; }int Cal2(int h, int w) {int l = 1, r = n, mid;if (h <= q[l].h) return -1;while (l < r){mid = l + r + 1 >> 1;if (q[mid].h < h){l = mid;}else{r = mid - 1;}}if (w > pre_w[l]) // 找到了一個高度小,寬度也小的return pre_idx[l];else // 沒有寬度小的return -1; }int Cal(int h, int w) {int res1 = Cal2(h, w), res2 = Cal2(w, h);if (res1 == -1 && res2 == -1)return -1;elsereturn max(res1, res2); }int main() {int t; cin >> t;while (t -- ){scanf("%d", &n);for (int i = 1; i <= n; i ++ ){scanf("%d%d", &a[i].h, &a[i].w);a[i].idx = i;q[i] = a[i];}sort(q + 1, q + 1 + n, cmp);pre_w[0] = INF, pre_idx[0] = -1;for (int i = 1; i <= n; i ++ ){if (q[i].w < pre_w[i - 1]){pre_w[i] = q[i].w, pre_idx[i] = q[i].idx;}else{pre_w[i] = pre_w[i - 1], pre_idx[i] = pre_idx[i - 1];}}/*for (int i = 1; i <= n; i ++ )printf("h=%d, w=%d, idx=%d, pre_w=%d, pre_idx=%d\n", q[i].h, q[i].w, q[i].idx, pre_w[i], pre_idx[i]);*/printf("%d", Cal(a[1].h, a[1].w));for (int i = 2; i <= n; i ++ ){printf(" %d", Cal(a[i].h, a[i].w));}puts("");}return 0; }F New Year’s Puzzle
原題信息
http://codeforces.com/contest/1472/problem/F
解題思路
首先第一個想到的就應該是對這些點按照列編號進行排序,然后我們逐個點進行處理。
用cur1,cur2cur1,cur2cur1,cur2表示當前第一行,第二行當前可以放置的最小列編號。下面我們對BlockedCellBlocked CellBlockedCell進行討論
- r1=r2r1 = r2r1=r2,即兩個BlockedCellBlockedCellBlockedCell將一整列堵住,那么要求abs(cur1?cur2)abs(cur1-cur2)abs(cur1?cur2)%2=02=02=0,最后更新cur1=cur2=r1+1cur1=cur2=r1+1cur1=cur2=r1+1即可,這一次處理了兩個BlockedCellBlockedCellBlockedCell問題
- r1!=r2r1 != r2r1!=r2,我們處理BlockedCell(r1,c1)BlockedCell(r1, c1)BlockedCell(r1,c1),假設這個BlockedCellBlockedCellBlockedCell在cur1cur1cur1那一列。首先倘若r1<cur1r1<cur1r1<cur1指定不行,因為小于cur1cur1cur1的區(qū)域已經使用完畢
那么如果(r1?cur2)(r1-cur2)(r1?cur2)%2=02=02=0,那么就說明這一行是可以自己解決的。
即使是使用了下面的那一行,也會是成對的使用,與另外一列自己解決一樣
倘若(r1?cur2)(r1-cur2)(r1?cur2)%2=12=12=1,也就是說他需要另外一列的幫助,加奇數的豎線排列,下面我們只需考慮是否可以加(至少)一個豎線,在這里只需要考慮加一個就行,因為多加了也只能是奇數個,奇數個豎線,兩兩可以合并為橫線,因為是加入一個,我們考慮一下最晚加入(在cur?1cur-1cur?1的位置)。
為什么是最晚在cur?1cur-1cur?1?因為最晚就是在 BlockedCell之前加入!
那么如何判斷是否可以,那么只需在最晚加入的話,另一個列前方時候可以組成成對個二元組即可
最后cur1,cur2cur1, cur2cur1,cur2注意更新,一個為c1c1c1,一個為c1+1c1 + 1c1+1 - 最后,我們還需考慮滿足了BlockedCellBlockedCellBlockedCell之后的cur1,cur2cur1, cur2cur1,cur2是否可以在結尾處ok,因此檢驗一下
(cur1?cur2)(cur1-cur2)(cur1?cur2)%2=02=02=0->成立,否則不成立
AC代碼
#include <bits/stdc++.h> using namespace std;const int M = 200010, INF = 0x3f3f3f3f; int n, m;class Node { public:int r, c; }q[M];bool cmp(const Node &t1, const Node &t2) {if (t1.c == t2.c)return t1.r < t2.r;elsereturn t1.c < t2.c; }bool Check() {// if (m & 1) return false;sort(q + 1, q + 1 + m, cmp); /*for (int i = 1; i <= m; i ++ )printf("I=%d, r=%d, c=%d\n", i, q[i].r, q[i].c); */int r1, r2, c1, c2;int cur1 = 1, cur2 = 1;q[m + 1].c = INF;for (int i = 1; i <= m; i += 1){/// printf("I=%d, cur1=%d, cur2=%d\n", i, cur1, cur2);c1 = q[i].c, r1 = q[i].r, c2 = q[i + 1].c, r2 = q[i + 1].r;if (c1 == c2){/// puts("Equal");i ++;if (abs(cur1 - cur2) & 1) // 口封不上return false;cur1 = cur2 = c1 + 1;}else{if (r1 == 1) // 上面{/// puts("Up");if (cur1 > c1) // 當前這個地方不能堵上return false;else{if ((c1 - cur1) % 2 == 0) // 偶數,可以自行解決{cur1 = c1 + 1;}else // 需要下面協(xié)助一個豎著的{cur1 = c1 - 1;if (cur1 - cur2 >= 0 && (cur1 - cur2) % 2 == 0) // 下面可以跟上{cur1 = c1 + 1;cur2 = c1;}else{return false;}}}}else // 下面{/// puts("Down");if (cur2 > c1) // 當前這個地方不能堵上return false;else{if ((c1 - cur2) % 2 == 0) // 偶數,可以自行解決{cur2 = c1 + 1;}else // 需要上面協(xié)助一個豎著的{cur2 = c1 - 1;/*printf("Down, cur1=%d, cur2=%d\n", cur1, cur2);printf("%d\n", cur2 - cur1 >= 0);printf("%d\n", ((cur2 - cur1) % 2 == 0));*/if ((cur2 - cur1 >= 0) && ((cur2 - cur1) % 2 == 0)) // 上面可以跟上{cur2 = c1 + 1;cur1 = c1;}else{//puts("FALSE");return false;}}}}}}if (abs(cur1 - cur2) & 1)return false;elsereturn true; }int main() {int t; cin >> t;while (t -- ){scanf("%d%d", &n, &m);for (int i = 1; i <= m; i ++ )scanf("%d%d", &q[i].r, &q[i].c);if (Check())puts("YES");elseputs("NO");}return 0; } /* 1 4 2 1 2 2 2 YES */ /* 1 4 3 1 1 1 2 2 2 NO */ /* 1 4 2 1 1 2 3 YES */ /* 3 5 2 2 2 1 4 3 2 2 1 2 3 6 4 2 1 2 3 2 4 2 6 */G Moving to the Capital
原題信息
http://codeforces.com/contest/1472/problem/G
解題思路
一個很有意思的dp問題
首先我們先利用bfs求出起點到其它任意各點的舉例,然后我們進行dp求解。
-
dp思路一
fi,0f_{i, 0}fi,0?表示從iii點開始,使用0次規(guī)則2可以達到的最小ddd,(其實就是自身的ddd)
fi,1f_{i, 1}fi,1?表示從iii點開始,使用1次規(guī)則2可以達到的最小ddd
下面我們來分析一下fi,1f_{i, 1}fi,1?如何進行更新,
fi,1={fj,0?i?j邊且di≥djfj,1?i?j邊且di<djf_{i, 1}=\begin{cases} f_{j, 0} & \exists i \Rightarrow j 邊且d_i\geq d_j\\ f_{j, 1} & \exists i \Rightarrow j 邊且d_i < d_j\\ \end{cases}fi,1?={fj,0?fj,1???i?j邊且di?≥dj??i?j邊且di?<dj??
fj,0f_{j, 0}fj,0?容易保證,因為直接就是djd_jdj?,但是我們如何保證使用的fj,1f_{j,1}fj,1?是更新完畢的最終結果呢? 下面我們考慮fj,1f_{j,1}fj,1?如何使其盡可能早的確定,而且不會使用到未確定的fj,1f_{j,1}fj,1?,我們從di,djd_i, d_jdi?,dj?在分段函數的關系中,就可以發(fā)現,倘若我們對ddd的距離進行降序排序,即先進性距離更大的dpdpdp,那么就可以是得使用到的fj,1f_{j,1}fj,1?都會是前面進行處理完畢的,因此進行dpdpdp的算法如下代碼 -
Algo
-
首先bfsbfsbfs處理出各點的距離did_idi?
-
處理出did_idi?之后,我們就可以進行按照距離進行非遞增排序。
-
按照分段函數進行dpdpdp
AC代碼
#include <bits/stdc++.h> using namespace std;const int N = 200010, INF = 0x3f3f3f3f; int f[N]; int h[N], e[N], ne[N], idx; int n, m; bool st[N]; int dist[N]; vector<int> order;void Initial() {// edge initialmemset(h, -1, sizeof h);idx = 0;// dp initialmemset(f, 0x3f, sizeof f);// dist initialmemset(dist, -1, sizeof dist);// orderorder.clear(); }void add(int a, int b) {e[idx] = b, ne[idx] = h[a], h[a] = idx ++; }int main() {int t; cin >> t;while (t -- ){// inputscanf("%d%d", &n, &m);// initialInitial();for (int i = 1, u, v; i <= m; i ++ ){scanf("%d%d", &u, &v);add(u, v);}// bfs for distqueue<int> que;que.push(1); dist[1] = 0;order.push_back(1);int u, v;while (que.size()){u = que.front(); que.pop();for (int i = h[u]; ~i; i = ne[i]){v = e[i];if (dist[v] == -1){dist[v] = dist[u] + 1;que.push(v);order.push_back(v);}}}/*for (int i = 1; i <= n; i ++ )printf("i=%d dist=%d\n", i, dist[i]);*/// dfs for dp/*從距離最遠的開始,進行枚舉dp*/for (int j = n - 1, u, v; j >= 0; j -- ){u = order[j];f[u] = dist[u];for (int i = h[u]; ~i; i = ne[i]){v = e[i];if (dist[u] >= dist[v]) // u -> v 需要耗費一次機會, 只可以 取dist{f[u] = min(f[u], dist[v]);}else // 不需要消耗機會,直接最優(yōu)子結構{f[u] = min(f[u], f[v]);}}}// output/*for (int i = 1; i <= n; i ++ )printf("i=%d, f[i][0]=%d, f[i][1]=%d\n", i, f[i][0], f[i][1]);*/printf("%d", 0);for (int i = 2; i <= n; i ++ )printf(" %d", f[i]);puts("");}return 0; }總結
以上是生活随笔為你收集整理的Codeforces Round #693 (Div. 3)A~G解题报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: flyme8会更新Android版本吗,
- 下一篇: Flink常见流处理API