码迷,mamicode.com
首页 > 其他好文 > 详细

[题解][Codeforces]Good Bye 2019 简要题解

时间:2020-01-03 23:24:33      阅读:95      评论:0      收藏:0      [点我收藏+]

标签:最优   三角形   lin   直接   top   最大数   strlen   cout   scanf   

  • 构造题好评,虽然这把崩了

  • 原题解

A

题意

  • 二人游戏,一个人有 \(k_1\) 张牌,另一个人 \(k_2\) 张,满足 \(2\le k_1+k_2=n\le 100\),每张牌上有一个数,保证所有的牌上的数互不相同且在 \([1,n]\)

  • 每回合双方都会出一张牌,牌上数小的一方的牌会给牌上数大的一方

  • 拿到所有 \(n\) 张牌的一方赢得比赛

  • 求两人都采取最优策略的情况下谁会赢

  • 多组数据,数据组数 \(t\le100\)

做法:贪心

  • \(a\)\(b\) 为双方的最大数

  • 显然如果 \(a>b\) 那么 A 必然每次都出最大的牌,这样可以必胜,反过来也是一样

  • 于是判断 \(a\)\(b\) 的大小关系即可,\(O(tn)\)

代码

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

const int N = 105;

int n, m1, m2, a[N], b[N];

void work()
{
    read(n); read(m1); read(m2);
    for (int i = 1; i <= m1; i++) read(a[i]);
    for (int i = 1; i <= m2; i++) read(b[i]);
    std::sort(a + 1, a + m1 + 1); std::sort(b + 1, b + m2 + 1);
    puts(a[m1] > b[m2] ? "YES" : "NO");
}

int main()
{
    int T; read(T);
    while (T--) work();
    return 0;
}

B

题意

  • 给定一个 \(n\) 个数的非负整数序列 \(a\),求 \(a\) 的一个子区间使得区间内数的 \(\max\)\(\min\) 之差不小于区间长度,或者告知无解

  • 多组数据,\(\sum n\le2\times10^5\)\(0\le a_i\le10^9\)

做法:线段树+单调栈结论

  • 先判断所有的长度为 \(2\) 的子区间

  • 可以发现如果所有长度为 \(2\) 的子区间的极差都 \(\le 1\) 则一定无解,因为显然 \(n\) 个数的,相邻数之差绝对值不超过 \(1\) 的序列极差的最大值只能是 \(n-1\)(序列单调时取得最大值)

  • \(O(\sum n)\)

代码

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

template <class T>
inline T Abs(const T &a) {return a < 0 ? -a : a;}

const int N = 2e5 + 5;

int n, a[N];

void work()
{
    read(n);
    for (int i = 1; i <= n; i++) read(a[i]);
    for (int i = 1; i < n; i++) if (Abs(a[i] - a[i + 1]) >= 2)
        return (void) printf("YES\n%d %d\n", i, i + 1);
    puts("NO");
}

int main()
{
    int T; read(T);
    while (T--) work();
    return 0;
}

C

题意

  • 给定一个 \(n\) 个数的非负整数序列 \(a\)

  • 给出一种方案,加入不超过 \(3\)\([0,10^{18}]\) 内的数,使得所有数的和是所有数异或和的 \(2\)

  • 可以证明一定存在解

  • 多组数据,\(\sum n\le10^5\)\(0\le a_i\le10^9\)

做法:构造

  • 做法有很多种,这里给出一个加入 \(2\) 个数的方案

  • 设所有数的异或和为 \(x\),所有数的和为 \(s\),则加入的 \(2\) 个数分别为 \(x\)\(x+s\)

  • 这样所有数的异或和为 \(x\bigoplus x\bigoplus (x+s)=x+s\)

  • 所有数的和为 \(s+x+(x+s)=2(x+s)\)

  • 条件得到满足。\(O(\sum n)\)

代码

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

typedef long long ll;

const int N = 1e5 + 5;

int n, a[N];

void work()
{
    ll sum = 0; int xo = 0;
    read(n);
    for (int i = 1; i <= n; i++) read(a[i]), sum += a[i], xo ^= a[i];
    puts("2");
    printf("%d %lld\n", xo, sum + xo);
    
}

int main()
{
    int T; read(T);
    while (T--) work();
    return 0;
}

D

题意

  • 交互题

  • 有一个 \(k\) 以及一个隐藏的数 \(m\),还有一个 \(n\) 以及一个隐藏的 \(n\) 元序列,保证所有的数互不相同

  • 每次可以询问 \(k\) 个下标,得出序列这些下标上的数中,第 \(m\) 小的数对应的下标

  • 你需要用不超过 \(n\) 次操作询问出 \(m\)

  • \(1\le m\le k<n\le500\),序列的元素在 \([0,10^9]\) 范围内

做法:???(我也不知道叫什么)

  • 认真分析之后发现 \(n\) 是没有用的,只有 \(k<n\) 是有用的,于是我们可以把 \(n\) 当成 \(k+1\)

  • 这样不难发现合法的询问只有 \(k+1\)

  • 接下去应该也有很多种做法,这里只说一种

  • 先询问 \(1\dots k\) ,设第 \(m\) 小的数对应下标为 \(x\) ,然后把 \(1\dots k\) 中的 \(x\) 去掉换成 \(k+1\),可以得到 \(x\)\(k+1\) 的大小关系(利用两次询问出的第 \(m\) 小的数值进行判断)

  • 然后依次对于每个 \(i\in[1,k]-\{x\}\),询问 \([1,k]-\{i\}+\{k+1\}\),同样可以得出 \(i\)\(x\) 的大小关系

  • 这样就能得到 \(m\) 的值了,操作次数为 \(k+1\),复杂度 \(O(k^2)\)

代码

#include <bits/stdc++.h>

const int N = 505;

int n, k, pos, val, ans = 1;
bool com;

int main()
{
    int x, y;
    scanf("%d%d", &n, &k);
    printf("? ");
    for (int i = 1; i <= k; i++) printf("%d ", i);
    puts(""); fflush(stdout);
    scanf("%d%d", &pos, &val);
    printf("? ");
    for (int i = 1; i <= k; i++) if (i != pos) printf("%d ", i);
    printf("%d\n", k + 1); fflush(stdout);
    scanf("%d%d", &x, &y); com = y < val;
    for (int i = 1; i <= k; i++) if (i != pos)
    {
        printf("? ");
        for (int j = 1; j <= k; j++) if (j != i) printf("%d ", j);
        printf("%d\n", k + 1); fflush(stdout);
        scanf("%d%d", &x, &y);
        if ((x != pos) ^ com) ans++;
    }
    return printf("! %d\n", ans), 0;
}

E

题意

  • 平面上给定 \(n\) 个互不相同的点,坐标是绝对值不超过 \(10^6\) 的整数

  • 求一种把 \(n\) 个点划分成两个非空集合的方案,使得不存在四个点(可以相同)\(i,j,k,l\) 满足 \(i,j\) 属于同一个集合,\(k,l\) 不属于同一个集合,且 \(i\)\(j\) 的欧几里得距离与 \(k\)\(l\) 的欧几里得距离相等

  • 可以证明一定存在解

  • \(2\le n\le10^3\)

做法:构造

  • 首先发现正三角形无解

  • 从而得出坐标为整数是有用的

  • 看到两个集合又容易想到奇偶性

  • 考虑能否有一种方案,对于两个点 \(i,j\) ,如果距离的平方为偶数则在同一个集合内,否则不在同一个集合内

  • 这启发我们考虑模 \(2\) 意义下的加法。不难发现把两维坐标之和为奇数的放一个集合,偶数的放另一个集合可以满足条件

  • 如果两维坐标之和的奇偶性全相同怎么办?

  • 发现在这种情况下,把坐标系旋转 \(45\) 度并把坐标压缩到 \(\frac 1{\sqrt 2}\) 之后,所有的点坐标仍然是整数

  • 于是这样处理之后继续做

  • 每旋转一次之后任意两点之间的距离的平方都会除以 \(2\),所以复杂度 \(O(n\log d)\)\(d\) 为任意两点之间最小距离

代码

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

const int N = 1005;

int n, m, X[N], Y[N];

int main()
{
    read(n);
    for (int i = 1; i <= n; i++) read(X[i]), read(Y[i]);
    while (1)
    {
        int c0 = 0, c1 = 0;
        for (int i = 1; i <= n; i++)
            (X[i] + Y[i] & 1 ? c1 : c0)++;
        if (c0 && c1)
        {
            std::cout << c1 << std::endl;
            for (int i = 1; i <= n; i++) if (X[i] + Y[i] & 1)
                printf("%d ", i);
            return puts(""), 0;
        }
        for (int i = 1; i <= n; i++)
        {
            int x = X[i] + Y[i], y = X[i] - Y[i];
            X[i] = x - (c1 > 0) >> 1; Y[i] = y - (c1 > 0) >> 1;
        }
    }
    return 0;
}

F

题意

  • 给定一个 \(01\) 序列 \(s\)

  • 求有多少个子区间满足其包含至少一个 \(1\),并且 \(1\) 的个数是区间长度的约数

  • \(1\le |s|\le 2\times10^5\)

  • 时限 8s

做法:根号分治

  • 这题没开感觉十分亏

  • 第一部分:如何求 \(1\) 的个数不超过 \(S\) 的合法子区间数

  • 先枚举右端点,再枚举 \(1\le i\le S\) 表示 \(1\) 的个数

  • 显然右端点固定下来之后,满足区间内 \(1\) 的个数恰好为 \(i\) 的子区间的左端点是连续的一段

  • 于是这时产生的区间内 \(1\) 的个数恰好为 \(i\) 的子区间的长度也是连续的一段,设其为 \([u,v]\)

  • 那么要计入答案的就是 \([u,v]\)\(i\) 的倍数的个数,即 \(\lfloor\frac vi\rfloor-\lfloor\frac{u-1}i\rfloor\)

  • \(O(nS)\)

  • 第二部分:如何求 \(\frac{len}{cnt}\) 不超过 \(S\) 的合法子区间数,\(len\) 为区间长度,\(cnt\)\(1\) 的个数

  • 还是枚举 \(1\le\frac{len}{cnt}=i\le S\)

  • 考虑先给每个数一个权值 \(a_x=1-[s_x=1]\times i\)

  • 易得一个子区间的 \(\frac{len}{cnt}=i\) 当且仅当该区间的 \(a\) 之和为 \(0\)

  • 于是求出 \(a\) 有多少对相同的前缀和即可

  • \(O(nS\log S)\)sort)或 \(O(nS)\)(利用上哈希或 std::unordered_map

  • 考虑把这两部分暴力结合起来

  • 易得对于一个合法区间,其 \(cnt\)\(\frac{len}{cnt}\) 不可能同时超过 \(\sqrt n\)

  • 于是取 \(S=\lfloor\sqrt n\rfloor\),跑一遍第二种暴力

  • 然后再跑第一种暴力,注意直接算则会把 \(cnt\)\(\frac{len}{cnt}\) 都不超过 \(S\) 的子区间算重,需要限制长度不小于 \((i+1)S\)

  • \(O(n\sqrt n)\)

代码

#include <bits/stdc++.h>

typedef long long ll;

const int N = 2e5 + 5;

int n, S, a[N], top, stk[N];
ll ans, sum[N];
char s[N];

int main()
{
    scanf("%s", s + 1); n = strlen(s + 1);
    S = sqrt(n);
    for (int d = 1; d <= S; d++)
    {
        for (int i = 1; i <= n; i++) a[i] = 1 - (s[i] == '1' ? d : 0);
        sum[0] = 0;
        for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
        std::sort(sum, sum + n + 1);
        for (int i = 0; i <= n;)
        {
            int j = i;
            while (j <= n && sum[i] == sum[j]) j++;
            ans += 1ll * (j - i) * (j - i - 1) >> 1;
            i = j;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (s[i] == '1') stk[++top] = i;
        for (int j = 1; j <= S && j <= top; j++)
        {
            int l = i - stk[top - j + 1] + 1, r = i - stk[top - j];
            if (l < j * (S + 1)) l = j * (S + 1);
            if (l > r) continue;
            ans += r / j - (l - 1) / j;
        }
    }
    return std::cout << ans << std::endl, 0;
}

G

题意

  • 给定 \(n\) 个整数 \(a_{1\dots n}\),对于每个 \(i\) 都满足 \(i-n\le a_i\le i-1\)

  • 求这 \(n\) 个整数中选出一个非空子集使得和为 \(0\)

  • \(1\le n\le 10^6\)

做法:构造

  • 妙啊!!!\(\times 1\)(不过听说随机乱搞能过???)

  • 首先 \(i-n\le a_i\le i-1\) 相当于 \(1\le i-a_i\le n\)

  • 考虑建立图论模型:\(i\)\(i-a_i\) 连一条边,显然这是一个基环树森林

  • 考虑一个环,易得如果选出的子集为这个环上的点,环上每条边的贡献都是入点与出点的编号差,于是和为 \(0\)

  • 于是随便找一个环即可,\(O(n)\)

代码

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

const int N = 1e6 + 5;

int n, a[N], cnt;
bool vis[N];

void work()
{
    int u;
    read(n); cnt = 0;
    for (int i = 1; i <= n; i++) read(a[i]), a[i] = i - a[i];
    for (int i = 1; i <= n; i++) vis[i] = 0;
    for (u = 1; !vis[u]; u = a[u]) vis[u] = 1;
    for (int i = 1; i <= n; i++) vis[i] = 0;
    for (; !vis[u]; u = a[u]) vis[u] = 1, cnt++;
    for (int i = 1; i <= n; i++) vis[i] = 0;
    printf("%d\n", cnt);
    for (; !vis[u]; u = a[u]) vis[u] = 1, printf("%d ", u);
    puts("");
}

int main()
{
    int T; read(T);
    while (T--) work();
    return 0;
}

H

题意

  • 给定 \(n\) 个数 \(a_{1\dots n}\),每个数都是不超过 \(10^6\) 的正整数

  • 对于任意 \(1\le i<j\le n\),如果 \(a_i<a_j\) 则连边 \((i,j)\),求连通块数

  • \(q\) 次单点修改,你需要在每次修改之后输出上面问题的答案,保证任何时候这 \(n\) 个数互不相同

  • \(1\le n,q\le 5\times10^5\)

  • 时限 8s

做法:线段树维护连续段

  • 性质:每个连通块都是一段区间,且任意连通块内数的 \(\min\) 都大于下一个连通块内数的 \(\max\)

  • 证明略

  • 问题转化成有多少个 \(1\le i\le n\) 满足对于任意 \(j\le i,k>i\) 都有 \(a_j>a_k\)

  • 即整个序列前 \(i\) 大的数恰好占据了序列前 \(i\) 个位置

  • 考虑暴力,把所有的数从大到小插入

  • 易得组成恰好一个前缀连通块的条件为不存在 \(1\le i<n\) 使得 \(i\) 位置还没被插入,\(i+1\) 位置已经被插入

  • 于是考虑开一棵值域为 \([1,10^6]\) 的线段树,位置 \(h\) 储存插入了所有 \(\ge h\) 的数之后满足如上条件的 \(i\) 个数,并维护区间最小值及最小值个数

  • 对于所有 \(a_i<a_{i+1}\),我们让线段树的区间 \([a_i+1,a_{i+1}]\) 加一

  • 这样答案就是全体最小值的个数(如果最小值不为 \(0\) 则答案为 \(0\)

  • 修改时讨论 \(pos-1,pos,pos+1\) 三个位置的影响并重新进行区间加即可

  • 但上面的过程中忽略了一个条件,如果 \(h\) 不与任意的 \(a_i\) 相等,那么这样的 \(h\) 是不能计入答案的,所以实现时我们还要记录区间内有多少个 \(h\) 是能够计入答案的,每次修改时要对这个东西进行单点修改

  • \(O((n+q)\log a)\)

代码

#include <bits/stdc++.h>
#define p2 p << 1
#define p3 p << 1 | 1

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

const int N = 5e5 + 5, M = 4e6 + 17, L = 1e6;

int n, q, a[N], add[M], pts[M];
bool is[M];

struct elem
{
    int val, cnt;
    
    friend inline elem operator + (elem a, elem b)
    {
        if (a.val < b.val) return a;
        else if (a.val > b.val) return b;
        return (elem) {a.val, a.cnt + b.cnt};
    }
} T[M];

void build(int l, int r, int p)
{
    T[p].val = add[p] = 0;
    if (l == r) return (void) (T[p].cnt = pts[p] = is[l]);
    int mid = l + r >> 1;
    build(l, mid, p2); build(mid + 1, r, p3);
    T[p].cnt = pts[p] = pts[p2] + pts[p3];
}

void down(int p)
{
    add[p2] += add[p]; add[p3] += add[p];
    T[p2].val += add[p]; T[p3].val += add[p];
    add[p] = 0;
}

void modify(int l, int r, int pos, int v, int p)
{
    if (l == r) return (void) (pts[p] += v, T[p].cnt += v);
    int mid = l + r >> 1; down(p);
    if (pos <= mid) modify(l, mid, pos, v, p2);
    else modify(mid + 1, r, pos, v, p3);
    T[p] = T[p2] + T[p3];
}

void change(int l, int r, int s, int e, int v, int p)
{
    if (e < l || s > r) return;
    if (s <= l && r <= e) return (void) (add[p] += v, T[p].val += v);
    int mid = l + r >> 1; down(p);
    change(l, mid, s, e, v, p2); change(mid + 1, r, s, e, v, p3);
    T[p] = T[p2] + T[p3];
}

elem ask(int l, int r, int s, int e, int p)
{
    if (e < l || s > r) return (elem) {-1, 0};
    if (s <= l && r <= e) return T[p];
    int mid = l + r >> 1; down(p);
    return ask(l, mid, s, e, p2) + ask(mid + 1, r, s, e, p3);
}

int main()
{
    int pos, x; read(n); read(q);
    for (int i = 1; i <= n; i++) read(a[i]), is[a[i]] = 1;
    build(1, L, 1);
    for (int i = 1; i < n; i++) if (a[i] < a[i + 1])
        change(1, L, a[i] + 1, a[i + 1], 1, 1);
    while (q--)
    {
        read(pos); read(x);
        modify(1, L, a[pos], -1, 1); modify(1, L, x, 1, 1);
        if (pos > 1)
        {
            if (a[pos - 1] < a[pos]) change(1, L, a[pos - 1] + 1, a[pos], -1, 1);
            if (a[pos - 1] < x) change(1, L, a[pos - 1] + 1, x, 1, 1);
        }
        if (pos < n)
        {
            if (a[pos] < a[pos + 1]) change(1, L, a[pos] + 1, a[pos + 1], -1, 1);
            if (x < a[pos + 1]) change(1, L, x + 1, a[pos + 1], 1, 1);
        }
        a[pos] = x; printf("%d\n", T[1].val ? 0 : T[1].cnt);
    }
    return 0;
}

I

题意

  • 给定一个 \(2^k\times 2^k\) 的正整数矩阵 \(a\),每个数在 \([0,2^{60})\) 内,行列从 \(0\) 开始标号

  • 还有一个大小为 \(t\) 的二元组集合 \(S=\{(x,y)\}\),里面的元素互不相同且 \(1\le x,y\le 2^k\)

  • 每次操作可以选择一个位置 \((u,v)\) 和一个数 \(p\),对于每个 \((x,y)\in S\),让矩阵 \(((u+x)\bmod 2^k,(v+y)\bmod 2^k)\) 位置上的数异或上 \(p\)

  • 求把所有的数都变成 \(0\) 的最少操作次数

  • \(1\le k\le 9,1\le t\le\min(99,4^k)\)\(t\) 是奇数

  • 时限 5s

做法:异或 + 分治

  • 妙啊!!!\(\times 2\)

  • 为了方便讨论,先把二元组集合内的横纵坐标都减一

  • 设数组 \(f_{i,j}=\sum_{(x,y)\in S}a_{(i-x)\bmod2^k,(j-y)\bmod2^k}\)

  • 性质 \(1\):当 \(t\) 为奇数时,\(a\)\(0\) 当且仅当 \(f\)\(0\)

  • 证明咕咕咕(我还没看),可以看原题解

  • 性质 \(2\):对 \((i,j)\) 进行异或 \(p\) 操作的影响是对于每个 \((x,y)\in S\) 使 \(f_{(i+2x)\bmod2^k,(j+2y)\bmod2^k}\) 异或上 \(p\)

  • 首先直观地描述,对 \((i,j)\) 进行异或 \(p\) 操作相当于在 \(S\) 中分两次,每次选一个二元组,设这两个二元组分别为 \((x,y)\)\((u,v)\),让 \(f_{(i+x+u)\bmod2^k,(j+y+v)\bmod2^k}\) 异或上 \(p\)

  • \((x,y)\ne(u,v)\),则两次分别选出 \((x,y)(u,v)\) 和分别选出 \((u,v)(x,y)\) 的影响是一样的,根据异或运算的性质 \(p\bigotimes p=0\),可以得到如果 \((x,y)\ne(u,v)\) 的情况不造成影响

  • 于是只剩下了 \((x,y)=(u,v)\) 的情况,\(\text{Q.E.D.}\)

  • 观察性质 \(2\),容易发现如果把矩阵 \(f\) 按照行列号的奇偶性拆成 \(4\) 个部分,那么每个部分是独立的,可以分别递归做

  • 递归到 \(k=0\) 的时候判断矩阵的唯一元素是否为 \(0\) 即可计入答案

  • \(O(4^kkt)\)

  • 异或真的是最美妙的运算啊。

代码

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

typedef long long ll;
typedef std::vector<std::pair<int, int> > vp;
typedef std::vector<ll> vl;
typedef std::vector<vl> vvl;

const int N = 520;

int k, t, ans;
vl emp;
vvl a;
vp op;

vvl trans(vvl a, vp op)
{
    int n = a.size();
    vvl res;
    for (int i = 0; i < n; i++)
    {
        res.push_back(emp);
        for (int j = 0; j < n; j++)
        {
            res[i].push_back(0);
            for (int k = 0; k < t; k++)
                res[i][j] ^= a[i - op[k].first + n & n - 1]
                    [j - op[k].second + n & n - 1];
        }
    }
    return res;
}

void jjd(vvl a, vp op)
{
    if (a.size() == 1) return (void) (ans += (a[0][0] > 0));
    a = trans(a, op); int n = a.size();
    vvl b[2][2];
    for (int i = 0; i < n; i++)
    {
        b[i & 1][0].push_back(emp); b[i & 1][1].push_back(emp);
        for (int j = 0; j < n; j++)
            b[i & 1][j & 1][i >> 1].push_back(a[i][j]);
    }
    for (int i = 0; i < t; i++) op[i].first &= (n >> 1) - 1,
        op[i].second &= (n >> 1) - 1;
    for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++)
        jjd(b[i][j], op);
}

int main()
{
    read(k); ll x; int u, v;
    for (int i = 0; i < (1 << k); i++)
    {
        a.push_back(emp);
        for (int j = 0; j < (1 << k); j++)
            read(x), a[i].push_back(x);
    }
    read(t);
    for (int i = 1; i <= t; i++)
        read(u), read(v), op.push_back(std::make_pair(u - 1, v - 1));
    return std::cout << (jjd(a, op), ans) << std::endl, 0;
}

[题解][Codeforces]Good Bye 2019 简要题解

标签:最优   三角形   lin   直接   top   最大数   strlen   cout   scanf   

原文地址:https://www.cnblogs.com/xyz32768/p/12147147.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!