Codeforces Day1
Codeforces 訓練 Day1
Codeforces 1288 A Deadline
思路
觀察這個式子
讓其最小化,長得很像基本不等式,變形一下,
觀察一下,左邊的最小值會是,
之後就直接判斷就可
Codeforces 1288 B Yet Another Meme Problem
思路
而其中, ( 爲某一確定的數)
由此可知, ,而對於 沒有限制
所以,只需統計滿足上述條件的 即可
Codeforces 1288C Two Arrays
思路
設 爲當前構造到第 位,序列 的最後一位分別爲
由於序列 單調不降,序列 單調不升,這個狀態轉移非常好寫,同時由於單調性, 和 的轉移有很大一部分是相同的,所以只需要在轉移全部的 時,對於每一個 考慮在 中沒有出現的部分,然後就是 再加上沒有出現的部分即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
#include <cmath>
using std :: vector;
using std :: swap;
using std :: queue;
using std :: set;
using std :: map;
using std :: string;
using std :: unordered_map;
using std :: priority_queue;
template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}
template <typename Tp>void Read(Tp &x) {
Tp in = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {in = in * 10 + ch - '0'; ch = getchar();}
x = in * f;
}
const int SN = 1000 + 10;
const int SM = 10 + 2;
const int MOD = 1000000007;
typedef long long LL;
LL f[SM][SN][SN];
LL g[SN], h[SN];
int n, m;
int main(int argc, const char * argv[]) {
Read(n), Read(m);
for (int j = 1; j <= n; j++)
for (int k = j; k <= n; k++)
f[1][j][k] = 1;
for (int i = 2; i <= m; i++) {
for (int j = 1; j <= n; j++) {
for (int k = j; k <= n; k++) g[k] = f[i - 1][j][k];
for (int k = n - 1; k >= j; k--) g[k] = (g[k] + g[k + 1]) % MOD;
for (int k = j; k <= n; k++) {
f[i][j][k] = (f[i][j - 1][k] + g[k]) % MOD;
}
}
}
LL ans = 0;
for (int j = 1; j <= n; j++) {
for (int k = j; k <= n; k++)
ans = (ans + f[m][j][k]) % MOD;
}
printf ("%lld\n", ans);
return 0;
}
Codeforces 1288D Minimax Problem
思路
對於求最小值最大的問題,上來先二分
於是問題轉化爲,對於當前的數字矩陣,是否存在兩行,其對應位置上的 ,
如果 令
於是問題轉化爲 是否存在兩行,其
利用二進制壓位,同時做一個高位前綴和即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
#include <cmath>
using std :: vector;
using std :: swap;
using std :: queue;
using std :: set;
using std :: map;
using std :: string;
using std :: unordered_map;
using std :: priority_queue;
template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}
template <typename Tp>void Read(Tp &x) {
Tp in = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {in = in * 10 + ch - '0'; ch = getchar();}
x = in * f;
}
const int SN = 300000 + 10;
const int SM = 8 + 2;
int a[SN][SM];
int vis[1 << 9];
int n, m, ansx, ansy;
bool check(int k) {
memset(vis, 0, sizeof vis);
int std = (1 << m) - 1;
for (int i = 1; i <= n; i++) {
int now = 0;
for (int j = 0; j < m; j++)
if (a[i][j] >= k) now |= (1 << j);
vis[now] = i;
}
for (int sta = std; sta >= 0; sta--) {
if (!vis[sta]) continue ;
for (int j = 0; j < m; j++)
if ((sta & (1 << j)) && (!vis[sta ^ (1 << j)]))
vis[sta ^ (1 << j)] = vis[sta];
}
for (int i = 1; i <= n; i++) {
int now = 0;
for (int j = 0; j < m; j++)
if (a[i][j] >= k) now |= (1 << j);
if (vis[std ^ now]) {
ansx = i, ansy = vis[std ^ now];
return true;
}
}
return false;
}
int main(int argc, const char * argv[]) {
Read(n), Read(m);
for (int i = 1; i <= n; i++)
for (int j = 0; j < m; j++)
Read(a[i][j]);
int l = 0, r = 1000000000, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d %d\n", ansx, ansy);
return 0;
}
Codeforces 1288E Messenger Simulator
思路
考慮某個元素 ,對於位置最小值,發現如果 一直沒有被彈到最開始,則最小值就是其初始位置,否則被彈到開頭就是 (因爲一直不被彈,別的元素就會被彈到開頭, 的位置就至少會大於等於其初始位置)
現在解決最大值,我們發現對於被操作的數 其可能的位置最大值一定是在被某次操作之前(可能操作很多次,這裏是討論每一次),因爲沒有被操作時, 的位置關於時間是不降的,所以我們就只需要對每次操作前的位置取 即可
而對於沒有被操作的數,只需要在所有操作完成後,類似上面那樣即可
於是,我們需要實現,在某個位置上刪除一個數,添加一個數,查詢某個區間有多少個數,這裏直接用線段樹實現
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
#include <cmath>
using std :: vector;
using std :: swap;
using std :: queue;
using std :: set;
using std :: map;
using std :: string;
using std :: unordered_map;
using std :: priority_queue;
template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}
template <typename Tp>void Read(Tp &x) {
Tp in = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {in = in * 10 + ch - '0'; ch = getchar();}
x = in * f;
}
const int SN = 300000 + 10;
const int SM = SN << 1;
int sum[SM << 2];
int pre[SN], next[SN], a[SN], pos[SN];
bool vis[SN];
int n, m;
void Modify(int x, int C, int l, int r, int rt) {
if (l == r) {
sum[rt] += C;
return ;
}
int mid = (l + r) >> 1;
if (x <= mid) Modify(x, C, l, mid, rt << 1);
else Modify(x, C, mid + 1, r, rt << 1 | 1);
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int Query(int QL, int QR, int l, int r, int rt) {
if (QL <= l && QR >= r) return sum[rt];
int mid = (l + r) >> 1, ans = 0;
if (QL <= mid) ans += Query(QL, QR, l, mid, rt << 1);
if (QR > mid) ans += Query(QL, QR, mid + 1, r, rt << 1 | 1);
return ans;
}
int main(int argc, const char * argv[]) {
Read(n), Read(m);
for (int i = 1; i <= m; i++) Read(a[i]), vis[a[i]] = 1;
for (int i = 1; i <= n; i++)
if (vis[i]) pre[i] = 1, next[i] = i;
else pre[i] = i, next[i] = i;
for (int i = 1; i <= n; i++)
Modify(i + m, 1, 1, n + m, 1), pos[i] = i + m;
for (int i = 1; i <= m; i++) {
int now = a[i];
next[now] = Max(next[now], n - Query(pos[now] + 1, n + m, 1, n + m, 1));
Modify(pos[now], -1, 1, n + m, 1);
pos[now] = m - i + 1;
Modify(pos[now], 1, 1, n + m, 1);
}
for (int i = 1; i <= n; i++)
next[i] = Max(next[i], n - Query(pos[i] + 1, n + m, 1, n + m, 1));
for (int i = 1; i <= n; i++)
printf("%d %d\n", pre[i], next[i]);
return 0;
}