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;
}