• AtCoder Beginner Contest 258 A~H 题解


    D - Trophy

    题目大意

    有一个游戏,由 N N N个关卡组成。第 i i i个关卡由一个数对 ( A i , B i ) (A_i,B_i) (Ai,Bi)组成。

    要通过一个关卡,你必须先花 A i A_i Ai的时间看一次介绍。然后,用 B i B_i Bi的时间打通这个关卡。若想多次通过同一个关卡,则第一次需要看介绍,后面无需再看(即如果想打通第 i i i N N N次,则所需时间为 A i + N × B i A_i+N\times B_i Ai+N×Bi)。

    开始时,只有第一关被解锁。当你每打通一关,其下一关会自动解锁(最后一关除外)。求总共打通 X X X关的最少时间(重复通关也算)。

    1 ≤ N ≤ 2 × 1 0 5 1\le N\le 2\times 10^5 1N2×105
    1 ≤ A i , B i ≤ 1 0 9 1\le A_i,B_i\le 10^9 1Ai,Bi109
    1 ≤ X ≤ N 1\le X\le N 1XN

    输入格式

    N   X N~X N X
    A 1   B 1 A_1~B_1 A1 B1
    ⋮ \vdots
    A N   B N A_N~B_N AN BN

    输出格式

    输出答案。

    样例

    略,请自行前往AtCoder查看。

    分析

    仔细想想会发现,通过的方式都是先不重复通过某一关前所有关卡,再通过这一关一定次数,最终达到正好 X X X次。因此,我们利用前缀和,依次枚举每个关卡,最终时间复杂度可达 O ( n ) \mathcal O(n) O(n)

    代码

    #include <cstdio>
    #define INF 0x7FFFFFFFFFFFFFFFLL
    using namespace std;
    
    int main()
    {
    	int n, x;
    	scanf("%d%d", &n, &x);
    	if(x < n) n = x;
    	long long s = 0LL, ans = INF, cur;
    	for(int i=1; i<=n; i++)
    	{
    		int a, b;
    		scanf("%d%d", &a, &b);
    		if((cur = (s += a + b) + (long long)(x - i) * b) < ans)
    			ans = cur;
    	}
    	printf("%lld\n", ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    E - Packing Potatoes

    WJ...


    F - Main Street

    WJ...


    G - Triangle

    题目大意

    给定一张由 N N N个点组成的简单无向图 G G G和它的邻接矩阵 A A A

    什么是邻接矩阵

    • 邻接矩阵,顾名思义,即表示图中每两点之间关系的矩阵。
    • 如本题中 A i , j A_{i,j} Ai,j表示 i , j i,j i,j两点之间是否有边。 A i , j = 0 A_{i,j}=0 Ai,j=0表示无边, 1 1 1表示有边。
    • 一般来说,对于任意简单无向图 A i , i = 0 A_{i,i}=0 Ai,i=0 A i , j = A j , i A_{i,j}=A_{j,i} Ai,j=Aj,i ( i ≠ j i\ne j i=j)。

    求数对 ( i , j , k ) (i,j,k) (i,j,k)的个数,使得:

    • 1 ≤ i < j < k ≤ N 1\le i<j<k\le N 1i<j<kN
    • ( i , j , k ) (i,j,k) (i,j,k)三个点在图中组成一个三角形,即 i , j , k i,j,k i,j,k中任意两点之间有一条连边。

    3 ≤ N ≤ 3000 3\le N\le 3000 3N3000

    输入格式

    N N N
    A 1 , 1 A 1 , 2 … A 1 , N A_{1,1}A_{1,2}\dots A_{1,N} A1,1A1,2A1,N
    A 2 , 1 A 2 , 2 … A 2 , N A_{2,1}A_{2,2}\dots A_{2,N} A2,1A2,2A2,N
    ⋮ \vdots
    A N , 1 A N , 2 … A N , N A_{N,1}A_{N,2}\dots A_{N,N} AN,1AN,2AN,N(注意没有空格,如10110

    输出格式

    输出一个整数,即符合条件的数对 ( i , j , k ) (i,j,k) (i,j,k)的个数。

    样例

    略,请自行前往AtCoder查看。

    分析

    前言

    • 个人感觉这题其实是这场比赛中最有意思的题。题目不难,但很具有研究意义。
      这里我将从各个角度分析题目,也期待大家在评论区中分享其他方法。

    废话不多说,题解开始!

    题目说得太啰嗦,其实不用管什么图论,可以看成是给定 N × N N\times N N×N 01 01 01矩阵 A A A,求 A i , j = A i , k = A j , k = 1 A_{i,j}=A_{i,k}=A_{j,k}=1 Ai,j=Ai,k=Aj,k=1 ( i , j , k ) (i,j,k) (i,j,k)的个数。

    再来看数据范围。 N ≤ 3000 N\le 3000 N3000,则 O ( N 3 ) \mathcal O(N^3) O(N3)的朴素算法时间可达到 T ( 2.7 × 1 0 10 ) T(2.7\times 10^{10}) T(2.7×1010),显然无法通过。

    然而,事实并非如此。
    在仔细研究后发现,时间限制为 3 s 3\mathrm{s} 3s的题目可以使用复杂度稍高的算法。
    但是一般来说,极端情况下超过 T ( 1 0 10 ) T(10^{10}) T(1010)的算法无法通过。
    因此,也许是数据不够强吧,使用如下的优化可以恰好通过(#32949139 37764 K B , 2755 m s 37764\mathrm{KB},2755 \mathrm{ms} 37764KB,2755ms):

    #pragma GCC optimize("Ofast") // -Ofast 预编译优化
    #include <cstdio>
    #define maxn 3000 // 数组大小设置正好,减少内存占用
    using namespace std;
    
    // 题目中正常的邻接矩阵
    bool a[maxn][maxn];
    
    // 特殊邻接表存储,减少尝试次数
    // 这里不使用vector,会拖慢速度
    int G[maxn][maxn], sz[maxn];
    
    inline void print(const long long& x) // 快写-输出优化
    {
    	if(x > 9LL) print(x / 10);
    	putchar(x % 10 ^ 48);
    }
    
    int main()
    {
    	// getchar快读输入优化
    	int n = 0; char c;
    	while((c = getchar()) != '\n')
    		n = (n << 3) + (n << 1) + (c ^ 48);
    	for(int i=0; i<n; i++, getchar())
    		for(int j=0; j<n; j++)
    			if(getchar() == '1' && j > i) // j > i:只存一半,去掉重复
    				a[i][j] = 1, G[i][sz[i]++] = j;
    
    	// 注意答案数据类型使用long long
    	long long ans = 0LL;
    	for(int v=0; v<n; ++v)
    		for(int i=0; i<sz[v]; ++i) // 直接调取邻接表,省去不必要判断
    		{
    			int u = G[v][i];
    			for(int j=0; j<sz[u]; ++j)
    				if(a[v][G[u][j]])
    					ans ++;
    		}
    	print(ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    当然,这种方法并非每道题都能用,因此还是建议大家继续看正解。

    正解还是基于上面的朴素算法,可看作:

    • 依次遍历 A i , j = 1 A_{i,j}=1 Ai,j=1 ( i , j ) (i,j) (i,j) i < j i<j i<j
    • => 将答案加上 A [ i ] A[i] A[i] A [ j ] A[j] A[j]对应位置上都是 1 1 1的位置个数
    • 输出答案,除以3(去掉每个重复算的三次)

    那么别的地方都没办法,只有第二部可以使用bitset进行优化。详见代码。

    代码

    #include <cstdio>
    #include <bitset>
    #define maxn 3000
    using namespace std;
    
    bitset<maxn> a[maxn];
    
    int main()
    {
    	int n = 0; char c;
    	while((c = getchar()) != '\n')
    		n = (n << 3) + (n << 1) + (c ^ 48);
    	for(int i=0; i<n; i++, getchar())
    		for(int j=0; j<n; j++)
    			if(getchar() == '1')
    				a[i].set(j); // a[i][j] = 1
    	long long ans = 0LL;
    	for(int i=0; i+1<n; i++)
    		for(int j=i+1; j<n; j++)
    			if(a[i][j])
    				ans += (a[i] & a[j]).count(); // 取交集,数1的个数
    	printf("%lld\n", ans / 3LL);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    部分待更……

  • 相关阅读:
    基于PCIe的NVMe协议在FPGA中实现方法
    uniapp中扫码,进行网络判定,是否有网络扫码
    Kubernetes 集群日志管理
    MySQL介绍
    OpenMLDB + Jupyter Notebook:快速搭建机器学习应用
    使用 Docker 部署 TailChat 开源即时通讯平台
    LeetCode竞赛---第 366 场周赛
    【探索AI】二十四 深度学习之第7周:深度学习在实际应用中的案例
    【Meetup预告】OpenMLDB+37手游:一键查收实时特征计算场景案例及进阶使用攻略
    win7连接打印机0x0000011b错误的解决办法
  • 原文地址:https://blog.csdn.net/write_1m_lines/article/details/125582361