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)\) 的答案为

\[\sum_{j=l}^{r-k+1} \operatorname{min} \{c_l, c_{l+1}, \cdots, c_{j}\} \]

倒序线段树扫描线:区间取 \(\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;
}
posted @ 2024-09-13 16:34  梨愁浅浅  阅读(318)  评论(3编辑  收藏  举报