Codeforces Round #737 (Div. 2)ios
褒貶不一,我的認爲題目質量可觀,賽時只會 A~C 而後掉大分 😞優化
將一個沒有重複數字的數列分爲剛好兩個非空數列,求品均值之和的最大值。spa
不難證實最大的數單獨一列是最優的。code
判斷是否能夠將一個沒有重複數字的數列分爲剛好 \(K\) 個非空數列,再合併,使得它升序排列。ci
一開始讀錯題了,覺得能夠拆無限次,而後 \(K\geq 3\) 直接 puts("Yes")
,WA 了兩發 😢get
有一個比較巧妙的作法,考慮將一個數字映射爲它最終須要到的位置。string
那麼只有一個連續段是能夠分到一塊兒的,看最短段數是否 \(\leq K\) 便可。it
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1e5 + 10; int T, n, m, a[N], b[N]; int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar(); return x * f; } bool cmp(int x, int y){return a[x] < a[y];} int main(){ T = read(); while(T --){ n = read(), m = read(); for(int i = 1; i <= n; i ++) a[i] = read(), b[i] = i; sort(b + 1, b + n + 1, cmp); int t = 0; for(int i = 1; i <= n; i ++) if(i == 1 || b[i] != b[i - 1] + 1) t ++; if(t > m) puts("No"); else puts("Yes"); } return 0; }
給定 \(n\) 個 \(k\) 爲二進制數,能夠隨便填 \(0/1\),求使 \(a_1\&a_2\&\dots\&a_n\geq a_1\ {\rm xor}\ a_2\ {\rm xor}\dots\ {\rm xor}\ a_n\) 的方案數。io
分 嚴格大於 和 剛好等於 兩種狀況統計比較簡單。class
不難發現嚴格大於的狀況只出如今 \(n\) 爲偶數的時候,而且某一位上全 \(1\),前面位剛好等於,後面位隨意。
而剛好相等就是要麼某一位都爲 \(0/1\),那麼首先預處理推得:
\(M_1\):表示使 \(n\) 個位異或爲 \(0\) 的方案數 \(=\binom{n}{0}+\binom{n}{2}+\cdots\),即偶數個 \(1\)。
那麼不難發現使 \(n\) 個 \(i\) 位二進制串剛好相等的方案數爲:
設 \(f(i)\) 表示這個值,那麼對於嚴格大於的狀況(僅當 \(n\) 爲偶數時考慮):
最終 \(Ans=S+f(k)\),時間複雜度 \(O(n+k\log n)\)。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N = 2e5 + 10; const LL MOD = 1e9 + 7; int T, n, k; LL fac[N], inv[N]; int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar(); return x * f; } LL Pow(LL a, LL b){ LL sum = 1; for(; b; b >>= 1){ if(b & 1) sum = sum * a % MOD; a = a * a % MOD; } return sum; } LL C(int n, int m){if(n == m) return 1; return fac[n] * inv[m] % MOD * inv[n - m] % MOD;} void Work(){ fac[0] = 1; for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % MOD; inv[n] = Pow(fac[n], MOD - 2); for(int i = n - 1; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % MOD; LL M1 = 1; for(int i = 2; i <= n; i += 2) M1 = (M1 + C(n, i)) % MOD; LL ans = 0; if(!(n & 1)) for(int i = 1; i <= k; i ++) ans = (ans + Pow((M1 + ((n & 1) ? 1 : - 1) + MOD) % MOD, i - 1) * Pow(Pow(2, k - i), n) % MOD) % MOD; ans = ((ans + Pow((M1 + ((n & 1) ? 1 : - 1) + MOD) % MOD, k)) % MOD) % MOD; printf("%lld\n", ans); } int main(){ T = read(); while(T --){ n = read(), k = read(); Work(); } return 0; }
給定 \(n\) 個串,其中某些連續段爲 \(1\) 其它爲 \(0\),若兩行之間有同列均爲 \(1\) 則它們能夠相鄰。
要求刪去一些行使得矩陣合法,求最少刪除的行數,並給出任意一組方案。
看到數據範圍就放棄 DP 的思路了,仍是要敢想 + 大力優化。
設 \(f(i)\) 表示第 \(i\) 行的答案,那麼 \(f(i)=\max\limits _{1\leq j<i}\{f(j)\}+1\),要求 \(i,j\) 有對應位。
利用線段樹維護列的最大值,須要支持 區間賦值 + 區間求 max,再記錄一個 \(pre_i\) 來輸出方案。
同行之間先同時 Query
再同時 Modify
,防止重疊狀況的影響。時間複雜度 \(O(n\log m)\)。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; typedef pair<int, int> PII; #define MP make_pair #define X first #define Y second const int N = 3e5 + 10; int n, m, t, f[N], pre[N], b[N << 1]; PII dat[N << 3], cov[N << 3], Zero = MP(0, 0); bool vis[N]; vector<PII> G[N]; int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar(); return x * f; } void Push_Down(int p){ if(cov[p].X == 0) return; int l = p << 1, r = p << 1 | 1; dat[l] = max(dat[l], cov[p]), cov[l] = max(cov[l], cov[p]); dat[r] = max(dat[r], cov[p]), cov[r] = max(cov[r], cov[p]); cov[p] = MP(0, 0); } void Modify(int p, int l, int r, int L, int R, int v, int Pos){ if(L <= l && r <= R){ cov[p] = max(cov[p], MP(v, Pos)); dat[p] = max(dat[p], MP(v, Pos)); return; } Push_Down(p); int mid = (l + r) >> 1; if(L <= mid) Modify(p << 1, l, mid, L, R, v, Pos); if(R > mid) Modify(p << 1 | 1, mid + 1, r, L, R, v, Pos); dat[p] = max(dat[p << 1], dat[p << 1 | 1]); } PII Query(int p, int l, int r, int L, int R){ if(L <= l && r <= R) return dat[p]; Push_Down(p); int mid = (l + r) >> 1; if(L > mid) return Query(p << 1 | 1, mid + 1, r, L, R); if(R <= mid) return Query(p << 1, l, mid, L, R); return max(Query(p << 1, l, mid, L, R), Query(p << 1 | 1, mid + 1, r, L, R)); } int main(){ n = read(), m = read(); for(int i = 1; i <= m; i ++){ int p = read(), l = read(), r = read(); G[p].push_back(MP(l, r)); b[++ t] = l, b[++ t] = r; } sort(b + 1, b + t + 1); t = unique(b + 1, b + t + 1) - (b + 1); for(int i = 1; i <= n; i ++) for(int j = 0; j < (int) G[i].size(); j ++){ G[i][j].X = lower_bound(b + 1, b + t + 1, G[i][j].X) - b; G[i][j].Y = lower_bound(b + 1, b + t + 1, G[i][j].Y) - b; } for(int i = 1; i <= n; i ++){ f[i] = 0; for(int j = 0; j < (int) G[i].size(); j ++){ PII now = Query(1, 1, t, G[i][j].X, G[i][j].Y); if(now.X > f[i]) f[i] = now.X, pre[i] = now.Y; } if(!f[i]) f[i] = 1, pre[i] = i; else f[i] ++; for(int j = 0; j < (int) G[i].size(); j ++) Modify(1, 1, t, G[i][j].X, G[i][j].Y, f[i], i); } int Mx = 0, Pos; for(int i = 1; i <= n; i ++) if(f[i] > Mx) Mx = f[i], Pos = i; for(; ; Pos = pre[Pos]) if(vis[Pos]) break; else vis[Pos] = true; printf("%d\n", n - Mx); for(int i = 1; i <= n; i ++) if(!vis[i]) printf("%d ", i); puts(""); return 0; }
國際象棋,要求用一個
Queen
在 \(\leq 130\) 步內將死一個King
。其中
(hidden)King
的位置未知,可是它不能吃Queen
。
聽說 S 型亂搞就能過 System Test 😮
考慮一行行往下壓,直到 King
無處可走。
首先思考一下,若從左往右將一行掃一遍的時候,King
並無上下移動,那麼他確定離 Queen
至少兩行。
而後分解步驟:
King
下移就直接下移。King
全程沒有垂直移動就直接下移。King
上移,它至多移動到 Queen
下面一行,再重新掃一遍,就可以強制他向下移動。上一操做的次數有限,不難發現至多 \(8\) 次,每次操做掃描須要 \(8\) 步。(將後移動到最左邊也算一步)
每次換行也須要一步,至多 \(7\) 次換行,因此總次數 \(\leq 8(8+8)=128<130\)。
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int nowY; string Move(int x, int y){ cout << x << ' ' << y << endl; nowY = y; string s; cin >> s; return s; } bool Get(int i){ string s; for(int j = (nowY == 1) ? 2 : 1; j <= 8; j ++){ s = Move(i, j); if(s == "Done") return true; if(s.find("Up") != string::npos) return Get(i); if(s.find("Down") != string::npos) return false; } return false; } void Work(){ nowY = 1; string s; for(int i = 1; i <= 8; i ++){ s = Move(i, nowY); if(s == "Done") return; if(Get(i)) return; } } int main(){ int T; scanf("%d", &T); while(T --) Work(); return 0; }