首页 > 其他分享 >AtCoder ABC 366题解

AtCoder ABC 366题解

时间:2024-08-12 16:40:59浏览次数:10  
标签:AtCoder right 方程组 int 题解 sum 366 节点 left

前言

本文部分思路来自于网络,仅供参考。

A - Election 2

题目大意

给定两个市长候选人的票数,求结果是否已经确定。

解题思路

如果剩下的人全部投票给票少的人票少的人也不能赢,则结果就已经确定了。

code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n,t,a;
    scanf("%d%d%d",&n,&t,&a);
    if(n - t - a + min(t,a) < max(t,a)) {
        printf("Yes\n");
    } else {
        printf("No\n");
    }
    return 0;
}

B - Vertical Writing

题目大意

给定 $n$ 段英文,将这 $n$ 段英文从右往左,从上往下输出。

解题思路

定义一个数组 $C[i][j]$ 为第 $n - j + 1$ 段英文的第 $i$ 个字符。只需要把每一段英文填入 $C$ 数组,然后输出即可。

code

#include <bits/stdc++.h>
using namespace std;
string s[105];
char c[105][105];
int si[105];
int main() {
    int n,m = 0;
    cin >> n;
    for(int i = 1;i <= n;i++) {
        cin >> s[i];
        m = max((int)s[i].size(),m);
    }
    for(int i = 1;i <= m;i++) {
        for(int j = 1;j <= n;j++) {
            if(i > s[j].size()) {
                c[i][n - j + 1] = '*';
                continue;
            }
            c[i][n - j + 1] = s[j][i - 1];
            si[i] = max(si[i],n - j + 1);
        }
    }
    for (int i = 1; i <= m; i++) {
        for(int j = 1;j <= si[i];j++) {
            printf("%c",c[i][j]);
        }
        puts("");
    }
    return 0;
}

C - Balls and Bag Query

题目大意

有三种操作:

  • 1 x 在包里放入一个写了数字 $x$ 的球。
  • 2 x 从包里拿出一个写了数字 $x$ 的球。
  • 3 输出包里有几种球。

给定一个操作序列,按顺序执行操作。

解题思路

维护一个变量 $cnt$ ,表示包里有几种球。如果有一种球被拿完了,则 $cnt$ 减一;如果新拿进来一种球,则 $cnt$ 加一。每一次 3 操作输出 $cnt$ 即可。

code

#include <bits/stdc++.h>
using namespace std;
int cnt = 0;
int arr[1000005];
void add(int x) {
    arr[x]++;
    cnt += (arr[x] == 1); 
}
void del(int x) {
    arr[x]--;
    cnt -= (arr[x] == 0);
}
int main() {
    int q;
    scanf("%d",&q);
    int op,x;
    for(int i = 1;i <= q;i++) {
        scanf("%d",&op);
        if(op == 1) {
            scanf("%d",&x);
            add(x);
        } else if (op == 2) {
            scanf("%d", &x);
            del(x);
        } else {
            printf("%d\n",cnt);
        }
    }
    return 0;
}

D - Cuboid Sum Query

题目大意

给定一个三维数组 $A$ ,对于每一组输入进来的 $(Lx_i,Rx_i,Ly_i,Ry_i,Lz_i,Rz_i)$ ,输出:

$$\sum\limits_{x=Lx_i}{Rx_i}\sum\limits_{y=Ly_i}\sum\limits_{z=Lz_i}^{Rz_i} A[x][y][z]$$

解题思路

直接用三位前缀和优化即可。

code

#include <bits/stdc++.h>
using namespace std;
int a[105][105][105];
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                scanf("%d", &a[i][j][k]);
                a[i][j][k] +=
                    a[i - 1][j][k] + a[i][j - 1][k] +
                    a[i][j][k - 1] - a[i - 1][j - 1][k] -
                    a[i - 1][j][k - 1] -
                    a[i][j - 1][k - 1] +
                    a[i - 1][j - 1][k - 1];
            }
        }
    }
    int q, lx, rx, ly, ry, lz, rz;
    scanf("%d", &q);
    while (q--) {
        scanf("%d%d%d%d%d%d", &lx, &rx, &ly, &ry, &lz, &rz);
        printf("%d\n",
               a[rx][ry][rz] - a[lx - 1][ry][rz] -
                   a[rx][ly - 1][rz] - a[rx][ry][lz - 1] +
                   a[lx - 1][ly - 1][rz] +
                   a[lx - 1][ry][lz - 1] +
                   a[rx][ly - 1][lz - 1] -
                   a[lx - 1][ly - 1][lz - 1]);
    }
    return 0;
}

E - Manhattan Multifocal Ellipse

题目大意

给定 $N$ 个点 $(x_i,y_i)$ 和 $D$ ,求有多少个点 $(x,y)$ 使得 $\sum_{i = 1}^{N}\left|x - x_i\right| + \left|y-y_i\right| \leq D$ 。

解题思路

显然,$\sum_{i = 1}^{N}\left|x - x_i\right| + \left|y-y_i\right| = \sum_{i = 1}^{N}\left|x - x_i\right| + \sum_{i = 1}^{N}\left|y - y_i\right|$ ,所以可以把 $x$ 和 $y$ 分开算。

维护两个数组 $sumx$ 和 $sumy$ ,$sumx[i]$ 表示 $\sum_{j = 1}^{N}\left|i - x_j\right|$ , $sumy[i]$ 表示 $\sum_{j = 1}^{N}\left|i - y_j\right|$ 。其中,$sumx[i]$ 的值可以拆解为 $\sum_{x_j < i}\left(x_j - i\right) + \sum_{x_j \geq i}\left(i - x_j\right)$。计算这两项的时间复杂度为 $\textrm{O}(n)$ 。为了快速求出这两项,可以维护前缀和 $presum$ 、后缀和 $sufsum$ 和小于 $i$ 的 $x_j$ 个数 $it$,则 $sumx[i]$ 可以转化为 $it \cdot i - presum + sufsum - (n - it) \cdot i$ ,时间复杂度为 $\textrm{O}(1)$ 。同理,$sumy[i]$ 也可以这么优化。

得到了 $sumx$ 和 $sumy$ 后,可以通过枚举 $x$ ,求出符合条件的 $y$ 的个数,同时,使用 $sumx$ 和 $sumy$ 快速计算 $\sum_{i = 1}^{N}\left|x - x_i\right| + \left|y-y_i\right| $ 。最后,统计所有符合条件的 $(x,y)$ 个数并输出。

code

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int x[200005],y[200005];
LL sumx[5000000],sumy[5000000];
void clac(int a[],LL sa[],int maxx,int n) {
    sort(a + 1,a + 1 + n);
    int it = 1;
    LL ps = 0,ss = 0;
    for(int i = 1;i <= n;i++) {
        ss += a[i];
    }
    for(int i = -maxx;i <= maxx;i++) {
        while(it <= n && a[it] < i) {
            ps += a[it];
            ss -= a[it++];
        }
        LL sum = 1LL * (it - 1) * i - ps + ss -  1LL * (n - it + 1) * i;
        sa[i + maxx] = sum;
    }
    sort(sa,sa + 2 * maxx + 1);
}
int main() {
    int n,d,maxx = 0;
    scanf("%d%d",&n,&d);
    for(int i = 1;i <= n;i++) {
        scanf("%d%d",&x[i],&y[i]);
        maxx = max(maxx,max(abs(x[i]),abs(y[i])));
    }
    clac(x,sumx,maxx + d,n);
    clac(y,sumy,maxx + d,n);
    LL ans = 0;
    int j = 2 * (maxx + d);
    for(int i = 0;i <= 2 * (maxx + d);i++) {
        while(j >= 0 && sumy[j] + sumx[i] > d) {
            j--;
        }
        ans += j + 1;
    }
    printf("%lld\n",ans);
    return 0;
}

F - Maximum Composition

题目大意

给定 $2n$ 个整数 $A_{1-n}$ 和 $B_{1-n}$ ,在 $1$ 到 $n$ 中选出 $k$ 个数 $p = \lbrace p_1,\ p_2,\ ...,\ p_k\rbrace$ ,使得 $f_1(f_2(...:f_k(1):...))$ 的值最大,其中 $f_i(x)=A_ix+B_i$ 。

解题思路

考虑 $f_i(f_j(1))$ 和 $f_j(f_i(1))$ 哪个大。

假设 $f_i(f_j(1)) \geq f_j(f_i(1))$ ,则:
$$\begin{align}
A_i(A_j + B_j) + B_i &\geq A_j(A_i + B_i) + B_j \
A_iB_j + B_i &\geq A_jB_i + B_j \
A_iB_j - B_j &\geq A_jB_i - B_i \
B_j(A_i - 1) &\geq B_i(A_j - 1)
\end{align
}$$
即当 $B_j(A_i - 1) \geq B_i(A_j - 1)$ 时, $f_i(f_j(1)) \geq f_j(f_i(1))$ 。所以,可以按照这个规则进行排序,之后进行 01 背包。因为 01 背包是按照数组里物品的顺序进行选择的,所以排序后的 01 背包可以得到最大的值。

code

#include <bits/stdc++.h>
using namespace std;
using A = array<int, 2>;
A a[200005];
long long dp[15];
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &a[i][0], &a[i][1]);
    }
    sort(a + 1, a + 1 + n, [](const A &a, const A &b) {
        return (a[0] - 1) * b[1] < (b[0] - 1) * a[1];
    });
    dp[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = k; j >= 1; j--) {
            dp[j] =
                max(dp[j], a[i][0] * dp[j - 1] + a[i][1]);
        }
    }
    printf("%lld\n", dp[k]);
    return 0;
}

G - XOR Neighbors

题目大意

给定一个有 $n$ 个节点,$m$ 条边的简单无向图,需要在每一个节点上写一个介于 $1$ 到 $2^{60}-1$ 的数,使得每一个节点的相邻节点异或和为 $0$ 。

解题思路

定义一种“节点中的数”以便和“节点上写的数”区分。“节点中的数”只能是 $0$ 或 $1$ ,且每一个节点的相邻节点中的数的异或和为 $0$。

定义 $ans_i$ 为第 $i$ 个节点上面应该写什么数。为了让 $ans_i$ 的值不为 $0$ , 令 $ans_i$ 的二进制第 $v$ 位的意思为:如果第 $v$ 个节点中的数为 $1$ ,节点 $i$ 中的数应该是多少。 $ans_i$ 的表达方法可以参考状态压缩。

考虑如果第 $v$ 个节点中的数为 $1$ ,其他节点中的数是多少。

为了求出其他节点中的数,可以先根据数据构建一个方程组,第 $i$ 个方程的意义为当第 $i$ 个节点的相邻节点中的数异或和为 $0$ 时,第 $i$ 个节点的相邻节点中的数分别是多少。显然,这个构建出来的方程组将会有 $n$ 个方程。比如输入数据:

3 3
1 2
1 3
2 3

所对应的方程组为:

$$\begin{cases}
x_2 \oplus x_3 = 0 \
x_1 \oplus x_3 = 0\
x_1 \oplus x_2 = 0
\end{cases}$$

为了解出构建出来的方程组,可以使用高斯消元法,不懂高斯消元的可以看这里。上面这个方程组所对应的增广矩阵为:

$$\left[
\begin{array}{c}
0&1&1&|&0 \
1&0&1&|&0 \
1&1&0&|&0
\end{array}
\right]$$

其中矩阵的第 $a_{ij}$ 位也可以理解成节点 $i$ 和节点 $j$ 之间是否有边连接。在具体实现中可以利用这条性质生成增广矩阵。

为了让第 $v$ 个节点中的数为 $1$ ,可以在方程组里添加一个方程 $x_v = 1 $ 。在具体实现中,可以在增广矩阵数组的第 $n+1$ 行的第 $v$ 列和第 $n+1$ 列设为 $1$ ,从而达到在方程组添加方程的效果。

那么,添加这个方程对结果有影响吗?因为这个方程除了它所对应的行向量的第 $v$ 位和增广矩阵增加的列向量的 $n + 1$ 位为 $1$ 外,其他位置均为 $0$ ,所以它对其他的数没有影响;如果在处理过程中出现了一个 $x_i = 0$ 与这个方程相对,则当 $x_i = 1$ 时, $x_i = 0$ ,所以 $x_i$ 不可能等于 $1$ ,即节点 $i$ 中的数为 $0$ ,这是不符合要求的,所以这个方程组无解,加不加这个方程都无所谓,所以添加这个方程对结果没有影响。

对于上面这个方程组,当 $v = 2$ 时,方程组变成

$$\begin{cases}
x_2 \oplus x_3 = 0 \
x_1 \oplus x_3 = 0 \
x_1 \oplus x_2 = 0 \
x_2 = 1
\end{cases}$$
它所对应的增广矩阵为

$$\left[
\begin{array}{c}
0&1&1&|&0 \
1&0&1&|&0 \
1&1&0&|&0 \
0&1&0&|&1
\end{array}
\right]$$

上面的增广矩阵高斯消元后的矩阵如下:

$$\left[
\begin{array}{c}
1&0&0&|&1 \
0&1&0&|&1 \
0&0&1&|&1 \
0&0&0&|&0
\end{array}
\right]$$

因为方程组可能无解,但是高斯消元法不能够区分无解和多解,所以在完成高斯消元法后需要检验。如果增广矩阵的某一个行向量全部为 $0$ ,且它在增广矩阵中增加的列向量中对应的一位为 $1$ 时,即:

$$\left[
\begin{array}{c}
0&...&0&|&1
\end{array}
\right]$$

由于它对应的方程为 $0=1$ ,显然不成立,所以这个方程组无解,输出 No

如果方程组有解,则把增广矩阵增加的列向量中的结果用状态压缩的方法放入对应的节点的头上写的数的对应的二进制位中,再处理下一个二进制位。

注意: 由于可以填入的数的范围为 $1$ 到 $2^{60} - 1$ ,超过了 int 的范围,所以应该使用 long long 。且因为最大可以填入的数为 $2^{60}-1$ ,所以应该从位权为 $2^0$ 的二进制位开始计算,否则结果将会超出范围。

code

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
LL ans[65];
LL h[65][65];
LL g[65][65];
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1, u, v; i <= m; i++) {
        scanf("%d%d", &u, &v);
        h[u][v] = h[v][u] = 1;
    }
    for (int v = 1; v <= n; v++) {
        memset(g, 0, sizeof(g));
        g[n + 1][v] = g[n + 1][n + 1] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                g[i][j] = h[i][j];
            }
        }
        for (int i = 1; i <= n; i++) {
            int cur = -1;
            for (int j = i; j <= n + 1; j++) {
                if (g[j][i] == 1) {
                    cur = j;
                }
            }
            if (cur == -1) continue;
            swap(g[cur], g[i]);
            for (int j = 1; j <= n + 1; j++) {
                if (i != j && g[j][i]) {
                    for (int k = 1; k <= n + 1; k++) {
                        g[j][k] ^= g[i][k];
                    }
                }
            }
        }
        for (int i = 1; i <= n + 1; i++) {
            int cnt = 0;
            for (int j = 1; j <= n; j++) {
                if (g[i][j]) cnt++;
            }
            if (cnt == 0 && g[i][n + 1]) {
                printf("No\n");
                return 0;
            } else if (g[i][n + 1]) {
                ans[i] += g[i][n + 1] << (v - 1);
            }
        }
    }
    printf("Yes\n");
    for (int i = 1; i <= n; i++) {
        printf("%lld ", ans[i]);
    }
    puts("");
    return 0;
}

标签:AtCoder,right,方程组,int,题解,sum,366,节点,left
From: https://www.cnblogs.com/sxloiblog/p/18354447

相关文章

  • ABC366D 题解
    第一眼是想写\(kd-tree\)的。然后发现这就是一道三维前缀和的板子题。三维前缀和要想学习三维前缀和,我们首先得了解前缀和的概念,并且学会一维、二维前缀和。什么是前缀和前缀和是容斥原理的典型应用。这种优化方式可以使求和操作的时间复杂度降低到\(O(1)\)(但是需要提前预......
  • 题解:AT_abc351_f [ABC351F] Double Sum
    关于某c人士强制偷袭代码导致AT号被封,\({\color{red}\mathrm{警钟敲碎}}\)。题意一个长\(n\)的数组\(a\),求所有顺序对中两元素之差的和。分析听说有大佬2min切掉。很明显,暴力过不去。考虑计算每个元素的贡献。设\(id\)为该元素之前所有比它小的元素个数,\(sum\)表示这些......
  • tensorboard_logger库无法导入的问题解决
    一、问题描述最近在学习深度学习时,从大神们那里copy的代码中有用到tensorboard_logger这个库的东西,所以很自然地就用condainstall或者pip去安装它,但是结果是:python开源库里面没有这东西。这就让我很苦恼,所以只能自己动手,丰衣足食了。 二、解决方法首先找到tensorboard_logge......
  • ABC366 G - XOR Neighbors 题解
    发现题目实质上就是让你构造一组\(a_{1,2,\dots,n}\),有一些限制,要求一些\(a\)异或起来是\(0\)。看到\(n\le60\),果断列异或方程组,用异或高斯消元。具体地,有\(n\)个方程组,\(a_{i,j}\)表示第\(i\)个方程中\(j\)的系数。对于每一个变量\(i\),要把\(j>i\)的方程中的第......
  • 【题解】P3356 火星探险问题
    \(\large\mathfrak{1st.\Preamble|}\)前言这都什么年代了网络流24题居然还能写题解!个人认为这篇题解讲的还是比较详细的。\(\large\mathfrak{2nd.\Solution|}\)题解看到题目的第一眼,我的反应是这样的:这不跟深海机器人问题差不多吗?Ctrl-CCtrl-V秒了。不过我还是讲讲怎......
  • ABC366D 题解
    Solution题意简述给你一个正整数\(N\)和\(N^3\)个非负整数,表示为\(A_{x,y,z}\)其中\(1\leqx,y,z\leqN\)。您将得到以下格式的\(Q\)个查询,必须按顺序处理。对于第\(i\)次查询\((1\leqi\leqQ)\),您将得到一个整数元组\((Lx_i,Rx_i,Ly_i,Ry_i,Lz_i,......
  • 逆序对数列(P2513) - 题解
    [HAOI2009]逆序对数列原题链接题目描述对于一个数列\(\{a_i\}\),如果有\(i<j\)且\(a_i>a_j\),那么我们称\(a_i\)与\(a_j\)为一对逆序对数。若对于任意一个由\(1\simn\)自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为\(k\)的这样自然数数列到底......
  • ABC366F Maximum Composition 题解
    ABC366FMaximumComposition题解题目大意给定\(N\)个一次函数\(f_i(x)=a_ix+b_i\),从中选出\(K\)个函数\(f_{p_1},f_{p_2},\dots,f_{p_K}\),使得\(f_{p_1}(f_{p_2}(\dotsf_{p_K}))\)最大,求最大值。Solve考虑这样一种情况:我已经选定\(p_{k+1},p_{k+1},\dots,p_K\),现......
  • ABC366F 题解
    Solution题意简述现在有\(N\)个线性函数\(f_1,\dots,f_N\)。函数\(f_i(x)=A_ix+B_i\)。找到一个长度为\(K\)的序列\(p=(p_1,\dots,p_k)\),序列元素为\(K\)个大小在\([1,N]\)的不同整数。输出\(f_{p_1}(f_{p_2}(\dotsf_{p_k}(1)\dots))\)可能的最大值。思路贪心......
  • 爱因斯坦求和约定einsum简单例题解读
    概论在爱因斯坦求和约定或einsum()格式字符串中,所有的索引都可以分为两类:自由索引集和求和索引集。它们的区别很简单:自由索引是用于输出规范中的索引。它们与外层for循环相关联。求和索引是所有其他索引:它们出现在参数规范中,但不出现在输出规范中。之所以称为求和索引,是因......