Codeforces Round 971 (Div. 4)
A. Minimize!
输入 \(a,b\), 输出 \(b-a\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
void solve()
{
int a = read(), b = read();
printf("%d\n", b - a);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
B. osu!mania
给定 \(n\) 行,每行有 4 个字符,保证每行恰好有一个 #
,倒序输出每行的 #
所在的列数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
char s[6];
int ans[505], n;
void solve()
{
n = read();
for(int i = 1; i <= n; ++i)
{
scanf("%s", s + 1);
if(s[1] == '#') ans[n - i + 1] = 1;
if(s[2] == '#') ans[n - i + 1] = 2;
if(s[3] == '#') ans[n - i + 1] = 3;
if(s[4] == '#') ans[n - i + 1] = 4;
}
for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
printf("\n");
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
C. The Legend of Freya the Frog
最初在 \((0,0)\),奇数步沿着 \(x\) 轴跳,偶数步沿着 \(y\) 轴跳,每次最多跳 \(d\) 步,问最少需要多少步到达 \((x,y)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
void solve()
{
int x = read(), y = read(), K = read();
x = ceil(1.0 * x / K), y = ceil(1.0 * y / K);
if(x > y) printf("%d\n", 2 * x - 1);
else printf("%d\n", 2 * y);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
D. Satyam and Counting
给定 \(n\) 个点 \((x,y)\),保证 \(0 \le x \le n, 0 \le y \le 1\),求这些点构成的直角三角形的个数。
容易发现若存在 \((a,0),(a,1)\),则可以与 \((b,0)\) 或 \((b,1)\) 构成直角三角形,其中 \(a \ne b\)。
还有特殊的直角三角形 \((x-1,0),(x,1),(x+1,0)\) 或 \((x-1,1),(x,0),(x+1,1)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int n, c[N][2];
int pre[N][2], suf[N][2];
void solve()
{
n = read();
for(int i = 0; i <= n; ++i)
{
pre[i][0] = pre[i][1] = 0;
suf[i][0] = suf[i][1] = 0;
c[i][0] = c[i][1] = 0;
}
for(int i = 1; i <= n; ++i)
{
int x = read(), y = read();
c[x][y] = 1;
pre[x][y] = suf[x][y] = 1;
}
for(int i = 1; i <= n; ++i) pre[i][0] += pre[i - 1][0], pre[i][1] += pre[i - 1][1];
for(int i = n - 1; i >= 0; --i) suf[i][0] += suf[i + 1][0], suf[i][1] += suf[i + 1][1];
ll ans = 0;
for(int i = 0; i <= n; ++i)
{
if(i > 0 && c[i][0] && c[i][1]) ans += pre[i - 1][0] + pre[i - 1][1];
if(i < n && c[i][0] && c[i][1]) ans += suf[i + 1][0] + suf[i + 1][1];
if(i > 0 && i < n)
{
ans += c[i - 1][1] & c[i][0] & c[i + 1][1];
ans += c[i - 1][0] & c[i][1] & c[i + 1][0];
}
}
printf("%lld\n", ans);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
E. Klee's SUPER DUPER LARGE Array!!!
给定 \(n,k\),形成长度为 \(n\) 的序列 \(a\),其中 \(a_i = n+i-1\),\(\forall i\),求 \(x = \mid a_1 + a_2 + \cdots + a_i - a_{i+1} - \cdots - a_n \mid\) 的最小值。
注意到 \(x\) 随 \(i\) 的递增先递减后递增,即是单谷函数,整数域上三分即可。
注意:整数域上三分结束条件不宜确定,可以在 \(r - l \le 5\) 时暴力枚举。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
ll get(ll l, ll r){ return (l + r) * (r - l + 1) / 2; }
void solve()
{
ll n = read(), K = read();
int l = 1, r = n;
while(r - l > 5)
{
int d = (r - l) / 3, lmid = l + d, rmid = r - d;
if(llabs(get(K, K + lmid - 1) - get(K + lmid, K + n - 1)) <= llabs(get(K, K + rmid - 1) - get(K + rmid, K + n - 1))) r = rmid;
else l = lmid;
}
ll ans = 0x7fffffffffffffff;
while(l <= r) ans = min(ans, llabs(get(K, K + l - 1) - get(K + l, K + n - 1))), ++l;
printf("%lld\n", ans);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
F. Firefly's Queries
给定一个长度为 \(n\) 的序列 \(a\),记序列 \(c_i = \{a_i, a_{i+1}, \cdots a_{n}, a_{1}, \cdots a_{i-1} \}\)。
记序列 \(b\) 为 \(c_1 + c_2 + \cdots + c_n\),+
表示拼接。
\(q\) 次询问,每次给定 \(l, r\),输出 \(b_l + b_{l+1} + \cdots + b_r\)
前缀和处理,模拟
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int a[N << 1], n, q;
ll sum[N << 1], S;
ll get(int id, int l, int r)
{
return sum[r + id - 1] - sum[l + id - 2];
}
void solve()
{
n = read(), q = read(), S = 0;
for(int i = 1; i <= n; ++i)
{
a[i] = read();
sum[i] = sum[i - 1] + a[i];
S += a[i];
}
for(int i = n + 1; i <= (n << 1); ++i)
{
a[i] = a[i - n];
sum[i] = sum[i - 1] + a[i];
}
while(q--)
{
ll l = read(), r = read();
ll ans = 0;
if((l - 1) % n != 0)
{
int id = (l - 1) / n + 1;
ans += get(id, (l - 1) % n + 1, n);
l += n - (l - 1) % n;
}
if(r % n != 0)
{
int id = (r - 1) / n + 1;
ans += get(id, 1, (r - 1) % n + 1);
r -= r % n;
}
ans += (r - l + 1) / n * S;
printf("%lld\n", ans);
}
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
G1. Yunli's Subarray Queries (easy version)
记 \(f(a)\) 表示修改 \(a\) 序列中的若干的元素,使序列 \(a\) 的一个长度至少为 \(k\) 的子序列成为公差为 1 的等差数列,本题中保证 \(a\) 序列长度为 \(k\)。
注意到令 \(b_i = a_i - i\),最终目标是序列 \(b\) 所有的数都相等,记序列 \(a\) 的众数出现次数为 \(c\) ,则 \(f(a) = k - c\)。
滑动窗口求众数出现次数,先离散化,记 \(cnt1[i]\) 为 \(i\) 的出现次数,记 \(cnt2[i]\) 为出现次数为 \(i\) 的数字个数,指针维护众数出现次数即可。
或者权值线段树:单点加+求全局 \(\operatorname{max}\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int n, K, q;
int a[N], b[N];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int mx[N << 2];
void update(int k, int l, int r, int pos, int val)
{
if(l == r){ mx[k] += val; return ; }
int mid = (l + r) >> 1;
if(pos <= mid) update(ls(k), l, mid, pos, val);
else update(rs(k), mid + 1, r, pos, val);
mx[k] = max(mx[ls(k)], mx[rs(k)]);
}
int ans[N];
void solve()
{
n = read(), K = read(), q = read();
for(int i = 1; i <= n; ++i) b[i] = a[i] = read() - i;
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - (b + 1);
for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
int l = 1, r = 0;
while(r < l + K - 1)
{
++r;
update(1, 1, m, a[r], 1);
}
ans[1] = K - mx[1];
while(r < n)
{
update(1, 1, m, a[l], -1);
++l;
++r;
update(1, 1, m, a[r], 1);
ans[l] = K - mx[1];
}
while(q--)
{
int l = read(), r = read();
printf("%d\n", ans[l]);
}
while(l <= r)
{
update(1, 1, m, a[l], -1);
++l;
}
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
G2. Yunli's Subarray Queries (hard version)
记 \(c_i = f([a_i, a_{i+1}, \cdots, a_{i+k-1}])\)。
询问 \((l, r)\) 的答案为
倒序线段树扫描线:区间取 \(\operatorname{min}\),区间求和。
区间取 \(\operatorname{min}\) 不易操作,可以利用单调性实现找到第一个大于某个值的位置+区间覆盖。
还可以只用单调栈,每个元素记录栈顶到栈底的和。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int inf = 0x7fffffff;
const int N = 2e5 + 5;
int n, K, q;
int a[N], b[N], c[N];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int mx[N << 2];
void UP(int k, int l, int r, int pos, int val)
{
if(l == r){ mx[k] += val; return ; }
int mid = (l + r) >> 1;
if(pos <= mid) UP(ls(k), l, mid, pos, val);
else UP(rs(k), mid + 1, r, pos, val);
mx[k] = max(mx[ls(k)], mx[rs(k)]);
}
vector< pair<int, int> > L[N];
ll ans[N];
int mn[N << 2], lazy[N << 2];
ll sum[N << 2], len[N << 2];
void build(int k, int l, int r)
{
mn[k] = inf, lazy[k] = inf, sum[k] = 0, len[k] = r - l + 1;
if(l == r) return ;
int mid = (l + r) >> 1;
build(ls(k), l, mid), build(rs(k), mid + 1, r);
}
void pushdown(int k)
{
if(lazy[k] != inf)
{
mn[ls(k)] = mn[rs(k)] = lazy[k];
lazy[ls(k)] = lazy[rs(k)] = lazy[k];
sum[ls(k)] = len[ls(k)] * lazy[k];
sum[rs(k)] = len[rs(k)] * lazy[k];
lazy[k] = inf;
}
}
void pushup(int k)
{
mn[k] = min(mn[ls(k)], mn[rs(k)]);
sum[k] = sum[ls(k)] + sum[rs(k)];
}
int query(int k, int l, int r, int pos)
{
if(l == r) return mn[k];
pushdown(k);
int mid = (l + r) >> 1;
if(pos <= mid) return query(ls(k), l, mid, pos);
else return query(rs(k), mid + 1, r, pos);
}
void update(int k, int l, int r, int L, int R, int val)
{
if(L <= l && r <= R)
{
mn[k] = lazy[k] = val;
sum[k] = len[k] * val;
return ;
}
pushdown(k);
int mid = (l + r) >> 1;
if(L <= mid) update(ls(k), l, mid, L, R, val);
if(R > mid) update(rs(k), mid + 1, r, L, R, val);
pushup(k);
}
int get(int k, int l, int r, int val)
{
if(l == r) return l - 1;
pushdown(k);
int mid = (l + r) >> 1;
if(mn[ls(k)] <= val) return get(ls(k), l, mid, val);
else return get(rs(k), mid + 1, r, val);
}
ll getsum(int k, int l, int r, int L, int R)
{
if(L <= l && r <= R) return sum[k];
pushdown(k);
int mid = (l + r) >> 1;
if(R <= mid) return getsum(ls(k), l, mid, L, R);
if(L > mid) return getsum(rs(k), mid + 1, r, L, R);
return getsum(ls(k), l, mid, L, R) + getsum(rs(k), mid + 1, r, L, R);
}
void solve()
{
n = read(), K = read(), q = read();
for(int i = 1; i <= n; ++i) b[i] = a[i] = read() - i;
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - (b + 1);
for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
int l = 1, r = 0;
while(r < l + K - 1) ++r, UP(1, 1, m, a[r], 1);
c[1] = K - mx[1];
while(r < n)
{
UP(1, 1, m, a[l], -1), ++l;
++r, UP(1, 1, m, a[r], 1);
c[l] = K - mx[1];
}
while(l <= r){ UP(1, 1, m, a[l], -1), ++l; }
for(int i = 1; i <= n - K + 1; ++i) L[i].clear();
for(int i = 1; i <= q; ++i)
{
int l = read(), r = read();
L[l].emplace_back(pair<int, int>(r, i));
}
build(1, 1, n - K + 2);
for(int i = n - K + 1; i >= 1; --i)
{
int R = get(1, 1, n - K + 2, c[i]);
update(1, 1, n - K + 2, i, R, c[i]);
for(auto [RR, id] : L[i]) ans[id] = getsum(1, 1, n - K + 2, i, RR - K + 1);
}
for(int i = 1; i <= q; ++i) printf("%lld\n", ans[i]);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
G3. Yunli's Subarray Queries (extreme version)
按照G2转化完变成询问区间 \([l, r]\) 的所有子区间的 \(\operatorname{min}\) 的和。
找到原题:P3246 [HNOI2016] 序列
参考:yijan的博客(在线做法)
所有元素按照二元组 \((a_i, i)\) 作为比大小依据。
考虑区间 \([l, r]\),找到区间的最小值所在位置 \(pos\)。
将所有子区间分成三类:
1.左右端点跨过 \(pos\) 的区间。
2.左右端点都在 \([l, pos - 1]\) 的区间。
3.左右端点都在 \([pos + 1, r]\) 的区间。
对于第一类区间,显然答案为 \(a[pos] \times (r - pos + 1)(pos - l + 1)\)。
对于第二类区间,记 \([l, r][L, R]\) 表示左端点在 \([l, r]\),右端点在 \([L, R]\) 的区间的最小值的和。
第二类区间答案为 \([l, pos - 1][l, pos - 1]\),考虑差分成 \([l, n][l, n] - [pos, n][pos, n] - [l, pos - 1][pos, n]\)。
记 \(f(l) = [l, n][l, n]\),有 \(f(l) = f(l+1) + [l, l][l, n]\)。
记 \(F(l) = [l, l][l, n], p_i\) 为 \(i\) 位置后面第一个小于 \(a_i\) 的数的位置,有 \(F(l) = F(p_l) + (p_l - l) \times a_l\)。
对于 \([l, pos - 1][pos, n]\),注意到 \([l, pos-1]\) 的数都大于 \(a_{pos}\) ,所以 \([l, pos - 1][pos, n] = (pos-l)F(pos)\),
对于 \([pos+1, r][pos+1, r]\) 倒着再做一遍即可。
感觉很妙啊,要求 \([l, r][l, r]\) 却去求 \([l, pos - 1][l, pos - 1]\),对于 \([l, pos - 1][pos, n]\) 的答案统计需要观察。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int inf = 0x7fffffff;
const int N = 2e5 + 5;
int n, K, q;
int a[N], b[N], c[N];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int mx[N << 2];
void UP(int k, int l, int r, int pos, int val)
{
if(l == r){ mx[k] += val; return ; }
int mid = (l + r) >> 1;
if(pos <= mid) UP(ls(k), l, mid, pos, val);
else UP(rs(k), mid + 1, r, pos, val);
mx[k] = max(mx[ls(k)], mx[rs(k)]);
}
namespace AAA
{
const ll inf = 0x7fffffff;
// const int N = 1e5 + 5;
int n, q, type;
ll a[N], b[N];
ll fa[N], Fa[N], fb[N], Fb[N];
ll sta[N], top, p[N];
void init(ll *a, ll *f, ll *F)
{
top = 0;
a[n + 1] = -inf, F[n + 1] = f[n + 1] = 0;
for(int i = 1; i <= n + 1; ++i)
{
while(top && a[sta[top]] > a[i]) p[sta[top]] = i, --top;
sta[++top] = i;
}
for(int i = n; i >= 1; --i) F[i] = F[p[i]] + (p[i] - i) * a[i];
for(int i = n; i >= 1; --i) f[i] = f[i + 1] + F[i];
}
int st[20][N], lg[N];
void initst()
{
lg[0] = -1;
for(int i = 1; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= n; ++i) st[0][i] = i;
for(int d = 1; d <= 19; ++d)
for(int i = 1; i + (1 << d) - 1 <= n; ++i)
if(a[st[d - 1][i]] <= a[st[d - 1][i + (1 << (d - 1))]]) st[d][i] = st[d - 1][i];
else st[d][i] = st[d - 1][i + (1 << (d - 1))];
}
int getpos(int l, int r)
{
int d = lg[r - l + 1];
if(a[st[d][l]] < a[st[d][r - (1 << d) + 1]]) return st[d][l];
if(a[st[d][l]] > a[st[d][r - (1 << d) + 1]]) return st[d][r - (1 << d) + 1];
return min(st[d][l], st[d][r - (1 << d) + 1]);
}
ll solve(ll l, ll r)
{
ll pos = getpos(l, r);
ll ans = (pos - l + 1) * (r - pos + 1) * a[pos];
ans += fa[l] - fa[pos] - (pos - l) * Fa[pos];
ans += fb[n - r + 1] - fb[n - pos + 1] - (r - pos) * Fb[n - pos + 1];
return ans;
}
void SOLVE()
{
initst();
init(a, fa, Fa), init(b, fb, Fb);
while(q--)
{
ull l = read(), r = read() - K + 1;
printf("%lld\n", solve(l, r));
}
}
}
void solve()
{
n = read(), K = read(), q = read();
for(int i = 1; i <= n; ++i) b[i] = a[i] = read() - i;
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - (b + 1);
for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
int l = 1, r = 0;
while(r < l + K - 1) ++r, UP(1, 1, m, a[r], 1);
c[1] = K - mx[1];
while(r < n)
{
UP(1, 1, m, a[l], -1), ++l;
++r, UP(1, 1, m, a[r], 1);
c[l] = K - mx[1];
}
while(l <= r){ UP(1, 1, m, a[l], -1), ++l; }
AAA::n = n - K + 1, AAA::q = q;
for(int i = 1; i <= n - K + 1; ++i) AAA::a[i] = c[i], AAA::b[i] = c[n - K + 1 - i + 1];
AAA::SOLVE();
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}