首页 > 其他分享 >AT_agc064_a题解

AT_agc064_a题解

时间:2023-08-16 16:25:14浏览次数:33  
标签:ch int 题解 top 交换 agc064 顶点 部分

题面

题目大意

给定一个正整数 \(N\),要求构造一个序列。对于每一个在 \(1\) 到 \(N\) 之间的整数 \(i\),序列中包含了 \(i\) 个,并且将该序列首尾相接拼成环后,相邻两项之差大于等于 \(1\) 小于等于 \(2\)。

思路

突破口是关于相邻两项之差的约束条件。

(我一开始竟然只看见了“小于等于 \(2\)”,然后我构造出来的相邻两项就有相等的情况,还调了半天 qwq)

感觉上“大于等于 \(1\)”是在让我们 递增递减,而“小于等于 \(2\)”则是给了我们一个 调整的空间

但是由于要求“包含 \(i\) 个 \(i\)”,那么就不能只递增或者只递减,肯定是要 反复递增递减

然后我就得到了一个 错误 的“图”(以 \(N=6\) 为例):

6         6         6         6         6         6
 5       5  5      5  5      5  5      5  5      5
  4     4    4    4    4    4    4    4    4    4
   3   3      3  3      3  3      3  3      3  3
    2 2        22        22        22        22
     1         1         1         1         1

(好像缩进有点问题……)

这很显然是 错误 的(其实当时就只是在脑子里想了一下,没画出来,但总是感觉哪里怪怪的,所以以后还是要勤动手啊……)

递增递减确实都考虑到了,但是数量却没有保证,那么下一步就考虑 先保证数量

于是又得到了下面的图(以 \(N = 6\) 和 \(N = 7\) 为例):

6 6 6 6 6 6
 5 5 5 5 5
  4 4 4 4
   3 3 3 
    2 2
     1
     
7 7 7 7 7 7 7
 6 6 6 6 6 6
  5 5 5 5 5
   4 4 4 4
    3 3 3
     2 2
      1

很明显的是,数量肯定是对的,但是要怎么把这个“图”转换成序列呢?

还是考虑 反复递增递减,然后可以发现 从左上到下方再到右上,这就是一个 先递减后递增 的过程。

然后就进行尝试,把整个图拆成下面的样子:

6         6  6     6  6 6
 5       5    5   5    5
  4     4      4 4
   3   3        3
    2 2
     1
     
7           7  7       7  7   7  7
 6         6    6     6    6 6   
  5       5      5   5      5
   4     4        4 4
    3   3          3
     2 2
      1

可以发现对于 奇偶不同 的 \(N\) 拆出来的图的样子是不太一样的,这个后面再说。

这时,感觉上拆开后的图好像差不多就可以了。

那到底差哪里了呢?

就是每一个部分的 连接处,如上图中就会有两个 \(6\) 挨着和两个 \(7\) 挨着的情况,这个时候就要用到出题人给的 调整的空间 了。

我们可以把一个“部分”中的某个数放到外面,具体地说,以“部分”“\(6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6\)”为例,我们可以把两个 \(5\) (或 \(4,3,2\))放到两个 \(6\) 的外侧。

这样做首先可以保证这个“部分”的中间几个位置是不破坏约束条件的,因为本身是递增或递减的,拿走其中一个之后,最多相差 \(2\)。如“\(5, 4, 3\)”拿走其中的 \(4\) 之后,\(5\) 和 \(3\) 之间也就差 \(2\)。

但是还有一个地方没有保证,那就是这个“部分”的两头。

还是以序列“\(6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6\)”为例,如果把 \(5\) 放在两头,那么 \(5\) 和 \(6\) 只差 \(1\),满足约束条件,但要是把 \(2\) 拿出来放在两头,那么 \(2\) 和 \(6\) 相差 \(4\),就不满足约束条件了。

同样的,把 \(4\) 拿出来也是满足条件的,但此处为了 简便,我们直接拿相差 \(1\) 的即可。

接下来讲一下“简便”的含义:

我们可以发现,每一个“部分”原始的两头都是 \(N\),所以我们把如“\(6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6\)”的序列加进去后只需进行两次“交换”:首项和第二项、倒数第二项和末项。

但是这里还有一个问题,那就是如果每一部分的两头都进行了这样的“交换”,就会“负负得正”,就变成了两个 \(N - 1\) 挨着,那么我们可以借用“拼图”的思想,就是一头设计成凸出去的,另一头是凹进来的。具体到这个题上就是,一头“交换”,另一头不“交换”

但是如果都这样设计也不行,因为在枚举到最后的“部分”的时候,如果规模太小,就进行不了“交换”。

例如“\(6, 5, 6\)”这个“部分”,如果进行了“交换”,那么两个 \(6\) 就会挨着。还有“\(7\)”这个“部分”,只有一个元素,无法进行“交换”。(\(N\) 的奇偶不同)那么像这样的情况,两头的值就必须都是 \(N\)。而与此类“部分”相接的“部分”是倒数第二个“部分”和第一个“部分”,那么倒数第二个“部分”的后面一头和第一个“部分”的前面一头就要设计成“交换”后的。

那么我们就可以把每一个“部分”的后面一头“交换”,前面一头不变(第一个“部分”两头都要“交换”)。

再讲一个实现上的细节。

怎样把一个“部分”加到要输出的数组里呢?

可以发现,每次都是从 \(N\) 到一个“顶点”,再从“顶点”到 \(N\)。比如“\(6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6\)”这个序列中的“顶点”就是 \(1\)。

那么我们就可以对“顶点”进行“监视”。

可以发现每个“顶点”都比前一个“部分”的“顶点”大 \(2\),但是在实现的时候不要直接写 +=2 ,而是分成两步:第一步是从 \(N\) 到“顶点”,然后将“顶点” ++ ,第二步就可以写成从 +1 之后的“顶点”到 \(N\)。

最后规模较小的“部分”暴力处理就好。

代码

含注释:

#include <bits/stdc++.h>

using namespace std;

int read() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ '0');
		ch = getchar();
	}
	return x * w;
}

void write(int x) {
	if (x < 0) {
		x = -x;
		putchar('-');
	}
	if (x > 9) write(x / 10);
	putchar(x % 10 ^ '0');
}

const int N = 500505;

int a[N];

signed main() {
	int n = read();
	int top = 1; // 顶点
	int idx; // 用来遍历 a 数组
	for (int i = n; i >= top; i -- ) { // 对于第一个“部分”的特殊处理
		a[ ++ idx] = i;
	}
	swap(a[1], a[2]);
	top ++ ; // 分成两步来处理
	for (int i = top; i <= n; i ++ ) {
		a[ ++ idx] = i;
	}
	swap(a[idx], a[idx - 1]);
	top ++ ;
	while (n - top > 1) { // 如果只剩两层或一层,就退出
		for (int i = n; i >= top; i -- ) {
			a[ ++ idx] = i;
		}
		top ++ ;
		for (int i = top; i <= n; i ++ ) {
			a[ ++ idx] = i;
		}
		swap(a[idx], a[idx - 1]);
		top ++ ;
	}
	if (n - top == 1) { // 这与 N 的奇偶性有关,要分类讨论
		a[ ++ idx] = n;
		a[ ++ idx] = n - 1;
		a[ ++ idx] = n;
	} else {
		a[ ++ idx] = n;
	}
	// write(idx), puts("");
	for (int i = 1; i <= idx; i ++ ) write(a[i]), putchar(' '); // 输出
	puts(""); // AtCoder 特性
	return 0;
}

复杂度是 \(O(L)\) 的,轻松过。

标签:ch,int,题解,top,交换,agc064,顶点,部分
From: https://www.cnblogs.com/Ifyoung/p/17635396.html

相关文章

  • CF1854D 题解
    CF1854DMichaelandHotel题解Links洛谷CodeforcesDescription这是一个交互题。有一个有\(n\)个点的内向基环树森林,zlsim位于\(1\)号节点,请你通过以下操作求出哪些节点(包括\(1\))可以通过从这两点开始沿边行走若干步汇至一点。给出两个参数\(u,k\)和点集\(S\),询......
  • P2034题解
    P2034题解题目描述给定一行\(n\)个非负整数\(a_1\cdotsa_n\)。现在你可以选择其中若干个数,但不能有超过\(k\)个连续的数字被选择。你的任务是使得选出的数字的和最大。题解正难则反,考虑将原问题转化为从\(a\)中选若干数使得,任意两数差不大于\(k\),求答案最小。观察......
  • ZS Shuffles Cards 题解
    ZSShufflesCards题解我们把每一次抽一些数字牌再抽到joker视作一局游戏。每局期望轮数首先考虑\(f_i\)表示每一局游戏抽出\(i\)张牌的概率。那么就是先抽出\(i-1\)张数字牌,再抽出一张joker。概率就是:\[f_i=\fracm{n+m-i+1}\prod_{k=0}^{i-2}......
  • CF1858A Buttons题解
    思路我们可以让两人先拿\(c\)里面的,因为\(a\)和\(b\)肯定是自己的,那么公共的“我”也要抢的越多越好,所以我们都要先拿\(c\)里面的。如果\(c\)是奇数,那么先手一定多拿\(1\)个\(c\)里面的,相当于先手可以拿\(a+1\)个,后手可以拿\(b\)个;如果\(c\)是偶数,那么两......
  • CF1858C Yet Another Permutation Problem 题解
    思路这个题是一个简单的构造题。竟然比T2简单,也是少见我们可以首先从\(1\)开始不断乘以\(2\),像这样:\(1,2,4,8,16\cdots,2^x\),直到什么时候超过\(n\)就停止。这样相邻两个数字就可以凑出\(1,2,4,6,\cdots,2^{x-1}\),保证两两不同。然后我们可以从\(3\)开始不......
  • 2023“钉耙编程”中国大学生算法设计超级联赛(9)- 1003 Reasoning 题解
    题目翻译基本符号有一推理系统,其中有这些符号:括号\((\)和\()\);逻辑连接词\(\lnot\)和\(\rightarrow\);全称量词\(\forall\);变量\(u\simz\);常量\(a\sime\);函数\(f\simh\);谓词\(P\simT\)。这些符号是构成系统的基础,他们之间能够组合出一些其他概念:项(term......
  • P3572题解
    P3572题解题面翻译有\(n\)棵树排成一排,第\(i\)棵树的高度是\(d_i\)。有\(q\)只鸟要从第\(1\)棵树到第\(n\)棵树。当第\(i\)只鸟在第\(j\)棵树时,它可以飞到第\(j+1,j+2,\cdots,j+k_i\)棵树。如果一只鸟飞到一颗高度大于等于当前树的树,那么它的劳累值会增......
  • CF1060E Sergey and Subway 题解
    题面由题意可知,在原图中经过边数为\(2\)的一对点,在新图中经过边数为\(1\)。所以每对点在新图中的距离为:\[\begin{aligned}\lceil\frac{dis(i,j)}{2}\rceil=\frac{dis(i,j)+dis(i,j)\;mod\;2}{2}\end{aligned}\]那么我们只需在原图上求出任意两点距离之和并加上\(dis......
  • 国标GB28181视频监控平台EasyGBS国标平台无法播放,抓包返回ICMP的问题解决方案
    国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台,可实现的视频功能包括:实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强,支持将接入的视频流进行全终端、全平台分发,分发的......
  • winform窗体闪烁问题解决方式
    winform窗体闪烁问题解决方式1、使用窗体双缓冲SetStyle(ControlStyles.UserPaint|ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer,true);UpdateStyles();窗体的DoubleBuffered 指示是否对控件进行双缓存处理。2、使用CreateParams的使用......