AtCoder Beginner Contest 247[題解D~G]
\(ABC247\)
\(D\)
題目大意
你有一個佇列,你需要完成 \(q\) 個操作,每種操作有如下兩種形式:
-
\(1\) \(x\) \(c\):在隊尾插入 \(c\) 個球,每個球上的數字為 \(x\)。
-
\(2\) \(c\):取出隊首 \(c\) 個球,並輸出這 \(c\) 個球上數字的和。
\(1\leq q\leq 2\times 10^5,0\leq x\leq 10^9,1\leq c\leq 10^9\)
保證取出的球不會超過現存的球。
\(Sol\)
用結構體包裝每個插入,取出的時候注意一下邊界等條件即可。
\(code\)
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10; inline int read() { int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar(); return s * w; } struct node{ int x, c; }sck[N]; int q, redc, redx; int st, ed, sum[N], cnt[N]; signed main() { q = read(); st = 1, ed = 0; for(register int i = 1; i <= q; i++){ int opt = read(); if(opt == 1){ int x = read(), c = read(); sck[++ed] = (node){x, c}, sum[ed] = sum[ed - 1] + c, cnt[ed] = cnt[ed - 1] + x * c; } else{ int c = read(); int l = st, r = ed, res = st - 1; while(l <= r){ int mid = (l + r) >> 1; if(sum[mid] - sum[st - 1] - redx <= c) res = mid, l = mid + 1; else r = mid - 1; } int ans = cnt[res] - cnt[st - 1] - redc; //cout << "res:" << res << "\n"; // cout << redc << " " << redx << "\n"; c = c - (sum[res] - sum[st - 1] - redx), st = res + 1; //cout << c << "\n"; ans = ans + c * sck[st].x; redc = c * sck[st].x, redx = c; printf("%lld\n", ans); } } return 0; }
\(E\)
題目大意
給定一組長為 \(n\) 的序列 \(A\),以及兩個整數 \(x\) 和 \(y\),詢問有多少個區間滿足區間最大值為 \(x\),最小值為 \(y\)。
\(1\leq n\leq 2 \times 10^5,1\leq A_i\leq 2\times 10 ^5,1\leq Y\leq X\leq 2\times 10 ^5\)
\(Sol\)
列舉左端點,線段樹二分或者 \(ST\) 表都可以快速確定符合條件的區間範圍。
\(code\)
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10, INF = 1e18; inline int read() { int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar(); return s * w; } struct Tree{ int mx, mi; }tr[4 * N]; int n, x, y, ans; int arr[N]; inline void build(int k, int l, int r) { if(l == r) { tr[k].mx = tr[k].mi = arr[l]; return; } int mid = (l + r) >> 1; build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r); tr[k].mx = max(tr[k << 1].mx, tr[k << 1 | 1].mx); tr[k].mi = min(tr[k << 1].mi, tr[k << 1 | 1].mi); } inline int askmx(int k, int l, int r, int x, int y) { if(r < x || l > y) return 0; if(l >= x && r <= y) return tr[k].mx; int mid = (l + r) >> 1; return max(askmx(k << 1, l, mid, x, y), askmx(k << 1 | 1, mid + 1, r, x, y)); } inline int askmi(int k, int l, int r, int x, int y) { if(r < x || l > y) return INF; if(l >= x && r <= y) return tr[k].mi; int mid = (l + r) >> 1; return min(askmi(k << 1, l, mid, x, y), askmi(k << 1 | 1, mid + 1, r, x, y)); } signed main() { n = read(), x = read(), y = read(); for(register int i = 1; i <= n; i++) arr[i] = read(); build(1, 1, n); for(register int i = 1; i <= n; i++){ //列舉做端點 // cout << "start:" << i << "\n"; int l = i, r = n, mxl = n + 1, mxr = 0, mil = n + 1, mir = 0; while(l <= r){ int mid = (l + r) >> 1, res = askmx(1, 1, n, i, mid); if(res == x) mxl = mid, r = mid - 1; if(res < x) l = mid + 1; if(res > x) r = mid - 1; } l = i, r = n; while(l <= r){ int mid = (l + r) >> 1, res = askmx(1, 1, n, i, mid); if(res == x) mxr = mid, l = mid + 1; if(res < x) l = mid + 1; if(res > x) r = mid - 1; } l = i, r = n; while(l <= r){ int mid = (l + r) >> 1, res = askmi(1, 1, n, i, mid); if(res == y) mir = mid, l = mid + 1; if(res > y) l = mid + 1; if(res < y) r = mid - 1; } l = i, r = n; while(l <= r){ int mid = (l + r) >> 1, res = askmi(1, 1, n, i, mid); if(res == y) mil = mid, r = mid - 1; if(res > y) l = mid + 1; if(res < y) r = mid - 1; } // cout << mxl << " " << mxr << "\n"; // cout << mil << " " << mir << "\n"; int L = max(mil, mxl), R = min(mir, mxr); if(L > R) continue; ans = ans + R - L + 1; } cout << ans << "\n"; return 0; }
\(F\)
題目大意
你有 \(n\) 張卡牌,第 \(i\) 張牌正面寫有一個數字 \(P_i\),背面寫有另一個數字 \(Q_i\)。
且陣列 \(P\) 和 \(Q\) 均為 \(1\) 到 \(n\) 的排列。
抽出若干張牌,要求所有牌正面和反面數字覆蓋 \(1\) 到 \(n\)。
\(1\leq n\leq 2 \times 10^5,1\leq P_i,Q_i\leq n\)
\(Sol\)
將正反面連邊,顯然原題意可轉化為邊覆蓋。
注意到每個點的度數必然為 \(2\),則建出的圖必然是若干個圈,這是顯然的。
對於每個圈,可以通過 \(dp\) 計算出邊覆蓋的方案數,最後的答案是每個圈的方案數的乘積。
\(code\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
struct edge{ int to, id; };
int n, tot, cnt, ans;
int P[N], Q[N];
int dp[N][2]; //dp[i][1/0] 表示第 i 條邊選或不選的方案數
bool vis[N], Judge[2 * N];
vector<edge> G[N];
inline void DFS(int u)
{
vis[u] = true;
for(register edge v : G[u]){
if(Judge[v.id]) continue;
cnt++, Judge[v.id] = true;
DFS(v.to);
}
}
signed main()
{
n = read();
for(register int i = 1; i <= n; i++) P[i] = read();
for(register int i = 1; i <= n; i++) Q[i] = read();
for(register int i = 1; i <= n; i++)
G[P[i]].push_back((edge){Q[i], ++tot}), G[Q[i]].push_back((edge){P[i], tot});
ans = 1;
for(register int i = 1; i <= n; i++){
if(!vis[i]){
cnt = 0, DFS(i);
if(cnt == 1) { ans = ans * cnt % mod; continue; }
int res = 0;
//欽定第一條邊不選
dp[1][0] = 1, dp[1][1] = 0;
for(register int i = 2; i < cnt; i++){ //最後一條邊必選
dp[i][1] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
dp[i][0] = dp[i - 1][1];
}
res = (dp[cnt - 1][0] + dp[cnt - 1][1]) % mod;
//欽定第一條邊選
dp[1][0] = 0, dp[1][1] = 1;
for(register int i = 2; i <= cnt; i++){ //最後一條邊選或不選都可以
dp[i][1] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
dp[i][0] = dp[i - 1][1];
}
res = (res + dp[cnt][0] + dp[cnt][1]) % mod;
ans = ans * res % mod;
}
}
printf("%lld\n", ans);
return 0;
}
\(G\)
題目大意
有 \(n\) 個人,第 \(i\) 個人屬於 \(A_i\) 大學,擅長 \(B_i\) 專業,能力值為 \(C_i\)。
定義一個合法的戰隊滿足一下兩個條件:
-
戰隊裡的任意兩個人不屬於同一個大學。
-
戰隊裡的任意兩個人不擅長同一個專業。
設一個合法戰隊的最大人數為 \(K\),對於所有人數小於等於 \(K\) 的戰隊,計算出當前人數合法戰隊的最大能力值。
\(1\leq n\leq 3\times 10^4,1\leq A_i,B_i\leq 150,1\leq C_i\leq 10^9\)
\(Sol\)
最大費用流模板題,需要注意的是我們怎麼計算從 \(1\) 到 \(K\) 的答案。
如果我們每次都重新建邊顯然會超時,考慮建立一個二分匯點,每次超級匯點向二號匯點連一條限流 \(1\) 的邊,就避免了清空等問題,網路流是一個會自己調整的過程,所以正確性顯然。
\(code\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e4 + 10, M = 2e5, INF = 1e18;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
struct Person{ int a, b, c; }P[N];
int n, pt, ca, cb, s, t, maxf, minc;
int cnt, ans[N];
int dis[N], Min[N], pre[N];
int tot = -1, v[2 * M], w[2 * M], c[2 * M], nex[2 * M], first[N];
bool vis[N];
map<int, int> ma, mb;
inline void Add(int x, int y, int z, int f)
{
nex[++tot] = first[x];
first[x] = tot, v[tot] = y, w[tot] = z, c[tot] = f;
}
inline bool SPFA()
{
for(register int i = 1; i <= pt; i++) dis[i] = INF, vis[i] = false;
queue<int> q;
q.push(s), vis[s] = true, dis[s] = 0, Min[s] = INF;
while(!q.empty()){
int x = q.front(); q.pop();
vis[x] = false;
for(register int i = first[x]; i != -1 ; i = nex[i]){
int to = v[i];
if(!w[i]) continue;
if(dis[to] > dis[x] + c[i]){
dis[to] = dis[x] + c[i];
Min[to] = min(Min[x], w[i]);
pre[to] = i;
if(!vis[to]) q.push(to), vis[to] = true;
}
}
}
return dis[t] != INF;
}
inline void dinic()
{
while(SPFA()){
maxf += Min[t], minc += dis[t] * Min[t];
int tem = t, i;
while(tem != s){
i = pre[tem];
w[i] -= Min[t], w[i ^ 1] += Min[t];
tem = v[i ^ 1];
}
}
}
signed main()
{
memset(first, -1, sizeof(first));
n = read();
for(register int i = 1; i <= n; i++){
P[i].a = read(), P[i].b = read(), P[i].c = read();
if(!ma[P[i].a]) ma[P[i].a] = ++ca;
if(!mb[P[i].b]) mb[P[i].b] = ++cb;
}
pt = n + ca + cb + 3, s = n + ca + cb + 1, t = s + 1;
for(register int i = 1; i <= n; i++){ //建邊
Add(ma[P[i].a], i + ca, 1, -P[i].c), Add(i + ca, ma[P[i].a], 0, P[i].c);
Add(i + ca, ca + n + mb[P[i].b], 1, 0), Add(ca + n + mb[P[i].b], i + ca, 0, 0);
}
for(register int i = 1; i <= ca; i++) Add(s, i, 1, 0), Add(i, s, 0, 0);
for(register int i = 1; i <= cb; i++) Add(i + ca + n, t + 1, 1, 0), Add(t + 1, i + ca + n, 0, 0);
for(register int i = 1, lst = 0; ; i++, lst = maxf){
Add(t + 1, t, 1, 0), Add(t, t + 1, 0, 0);
dinic();
if(maxf == lst) break;
ans[i] = -minc, lst = maxf;
}
printf("%lld\n", maxf);
for(register int i = 1; i <= maxf; i++){
printf("%lld\n", ans[i]);
}
return 0;
}