【题解】构造交互题汇总
构造
选图
题意:
给定一个 kn 个顶点的无向简单图,保证每个点的度数小于 kd。
请你求出一个 n 个点的子集,使得导出子图的每个点度数均小于 d。
\(n \in [1,1e3],d\in[1,10],k\in[2,10],m\le\frac{k(k-1)}{2}dn\)。
调整法构造。
由 \(k = 2\) 的部分出发,想到调整法构造。维护两个点集 \(a\),\(b\)。设 \(|a|=siz_a n,|b|=siz_bn\)(故 \(k = siz_a+siz_b\)),需要满足 \(\forall p \in a,degree_p < d \cdot siz_a, \forall p \in b,degree_p < d \cdot siz_b\)。若不满足,将不满足的多个点进行配对(\(x_i \in a, y_i \in b\)),在 \(a\),\(b\) 之间交换(交换后 \(x_i\) 和 \(y_i\) 必然合法【反证:\(x\) 的度数变为 \(edge(x,b)\),若不合法,即 \(edge(x,b)>dsiz_b\),则 \(mindegree(x) = edge(x,b)+edge(x,a) \ge siz_b d + siz_a d = k d\),与题目所给的限定不符,故必然合法,\(y\) 同理】),直至有一边满足条件。然后 \(k / 2\) 规模地对于合法的那一块分治下去。发现两个集合之间的边数是严格单调递增的,故复杂度为 \(O(nmd)\)。
给定直径个数构造树
一听到构造题,被吓怕了。就写了个单个菊花的。k 这么大一眼多个菊花。还有剩余打个表发现 2 或 3 个菊花满了,这就是正解。
KTSC 2024 R2 岛屿
简述题意:给定一个 \(n\) 多边形以及其三角划分,对于以这张图为基础进行如下构造:取出一个三角形 \((a,b,c)\),在其中心位置新建一个点 \(w\),连上 \((a,w),(b,w),(c,w)\)。进行若干次构造后使得整张图的边可以划分为两棵树,其中每棵树都包含所有节点。要求构造次数尽量少,且给出最后的两棵树。
分析:一开始是没有头绪的。但是构造题是给了层次的,我们完全可以构造一种方案。题目的硬性要求是在 \(n\) 次构造以内。那么 $A = $ 外围一圈少一条边,$B = $ 内部边和外围一条边,开始构造可以拿到部分分。思考为什么我们需要构造才可以划分出两棵树?对于三角划分,有多个个度数为 \(2\) 的点存在,手模发现不可能得到合法的划分。然而在所有情况下,最后只有一个度数为 \(2\) 的点不满足。所以只需要在那个地方进行一次构造。如何求方案?类似我们尝试去划分时,每次找到一个度数为 \(2\) 的点,对三角形染色,然后将这个三角形“删去”,变成子问题,即可求解。具体用队列实现。
Flop Sorting
与 NOIP2020移球游戏 相似的中转思想,找一个中转状态简化问题。
与 NOIP2022喵了个喵 相似的分治思想,子问题子结构。
发现这个操作交换区间内的最大最小值,这在两种情况下会利于理解:1. 交换两个相邻的数;2. 翻转一个有序数组。
设置中转状态为 \(1\sim n\),变成排序问题。根据数据范围,我们需要操作数 \(O(n \log n \sim n \log^2 n)\) 的算法。回顾快速排序的过程,每次用双指针处理出左边小于基准数右边大于基准数的两个子问题,递归求解。但是我们不能跨越无序区间进行 swap 啊。考虑归并排序的过程,每次将两个有序数组依次加入,但没有 swap 操作。结合一下,考虑以下构造(来自Luogu题解)。

NOIP2020 移球游戏
构造题
10pts:首先就是只有 \((2+1)\) 个柱子的情况(我模拟赛时没想到qwq)。
我们的目的是构造出两根上面都是颜色 \(1\) 和都是颜色 \(2\) 的柱子,然后将 \(2\) 号柱子上的 \(m\) 个分到这两根柱子上去。
这种情况两根上的东西一定就是原来 \(1\) 号的,那么第一步就是要将其分开。分开要腾出两个柱子,而 \(2\) 号柱子是满的,所以我们就可以把 \(2\) 号柱子当作垫脚的先放一部分到 \(3\) 号里去,这样 \(2\) 号就腾出位置来了。
具体的:若 1 中有 cnt 个颜色为 1 的,把 2 顶端的 cnt 个放到 3 号上去。再对 1 号进行出栈,颜色是 1 的全放到 2 号,颜色是 2 的全都放到 3 号。再将 2 号顶端的 cnt 个放回 1,3 号顶端的 m-cnt 个放回 1,3 号剩下垫脚的放回 2。再将 1 号顶端的 m-cnt 个颜色为 2 的放到 3。最后将 2 号的 m 个按颜色分到 1,3 上去。
tips:上面的都是废话,不如自己手模一下。
40pts:
策略就是对于每个规模为 \(n\) 的问题,将所有颜色为 \(n\) 的放到一个柱子上,缩小问题规模为 \(n-1\)。
一个想法:类似于两根柱子一样对于 \(i\) 和 \(n\) 两两在那边搞?不太行,因为颜色为 \(n\) 的个数不足 \(m\) 个,若放在那就会使得没有空的柱子可用。所以我们就要把所有颜色为 \(n\) 的球移到所有柱子上方。发现 10pts 中的转移中就有那么一种状态,即颜色为 \(n\) 的都在上面,颜色不为 \(n\) 的都在下面。但是我们拿来垫脚的那根柱子上是不能有颜色 \(n\) 的,那么一开始优先处理出这个用来垫脚的柱子即可。记录 \(id\),将柱子的角色转化用 swap id 解决,这样就避免了将球从一个柱子全都移到另一个空柱子上的浪费。复杂度为 \(O(t \times n^2m)\),\(t\) 为常数。
其实这种方法是可以过的,因为每次问题规模都在减小,奈何人傻常数大(其实可以加上一些策略的化简可以通过,由于我这里的每个步骤都是照搬 10pts 中的,所以有 5~6 的大常数)。
100pts:
其实也是构造题的套路:分治。40pts 中我们每次处理一个颜色的问题,瓶颈在于要把每根柱子的目标颜色移到该柱子上方。这是由于个数不足不能独立操作造成的。但是如果对颜色进行区分,设当前处理区间为 \([l,r]\),颜色编号为 \([l,mid]\) 的类比为 \(1\),编号为 \([mid+1,r]\) 的类比为 \(2\)。同样对于柱子的编号 \([l,mid]\) 的为左边柱子,\([mid+1,r]\) 的为右边柱子。对于一个左边柱子和一个右边柱子,无论如何我们都可以弄出一个都是颜色 \(1\) 的柱子或者一个都是颜色 \(2\) 的柱子出来。每一次操作都会完成一个柱子,那么对于规模为 \(n\) 的问题我们就可以在 \(n\) 次操作内使得左边柱子上的颜色编号都为 \(1\),右边都为 \(2\),递归解决即可。复杂度 \(O(t \times n m \log n)\)。
[CEOI 2023] Grading Server
由度数想到的欧拉回路构造。
\(S = 2\):每个评测机作为连接这两种颜色的一条边,由先评测的指向后评测的,最后要求每个颜色的出入度之差不超过 \(1\)。拿个超级源点连向度为奇数的点然后跑一遍欧拉回路。
正解:明显分治,对于左右区间依次选择对应的点连起来,同部分分做法,跑一遍确定每道题在哪个区间,然后分治下去。
交互
KTSC 2024 R2 回文判定
题意简述:半交互题。给定序列的长度为 \(n\)。询问1(A):可以进行询问 \((s_x,s_y,s_z)\) 得到其中相等的对数;询问2(B):也可以询问 \((x,vector V)\) 即是否存在 \(j \in V,s_j = s_x\)。要求第一个询问个数 \(\le \frac{n}{2} + 1\),第二个个数 \(\le 1\)。
分析:首先可以用第二个询问扫过来判断。然后考虑优化判断相等的过程。考虑从中间开始扩展,对已经完成的 \(a\),要判断是否 \(c = d\),具体过程为: 若 \(A(c,d,a) = 3\),成立;若 \(A(c,d,a) = 0\),直接退出;否则只有 \(A(c,d,a) = 1\),那么有两种可能:\(c = d \not= a,c = a \not= d\)。那么再利用 \(B\) 进行判断即可。发现对于确定的 \(a\),最后统一用 \(B\) 一次处理即可。但是第一次判断 \(a\) 难道还要用 \(B\) 吗?其实最后再判断 \(a\) 与其对应的 \(b\),由于其他数是否与 \(a\) 相等的关系已知,可以用 \(A\) 判断。
赛时没有深入地想,其实部分分的引导和不受定式的约束,自然的思考,都会给解题带来帮助。
服装
内核
- 注意:需要
fflush(stdout)或cout << endl刷新输出缓冲区。 - 交互题的套路:分治、二分、增量法。
题意简述
给定序列长度 \(n \in [2,150]\),你可以在一次询问里得到某一些点的本质不同颜色个数,格式为 k a_1 a_2 a_3 ... a_k。
让你确定序列每一个位置的颜色。询问次数要求 \(\le 3500\)。
分析
这里使用了增量法和倍增。考虑我们已经确定了前 \(i-1\) 个位置。(询问 \([l,r]\) 的结果记为 \(ask[l][r]\))
- 若 \(ask[1][i] = ask[1][i-1] + 1\),则说明前 \(i-1\) 个中没有与 \(a_i\) 相同颜色的,记下颜色为
++tot。 - 否则我们要找到与 \(a_i\) 相同的前一个位置,即找到最左边的一个 \(res\) 使得 \(ask[res][i] = ask[res][i-1] + 1\) 那么说明结果就是 \(res-1\)。那么并查集维护即可。
考试的时候没有方向,在乱搞。这里区间之间的关系与颜色的密切相关使我们使用了增量法,大大简化问题。
CODE
#include<bits/stdc++.h>
using namespace std;
const int N = 155;
int n,col[N];
int ask[N][N];
int qry(int l,int r){
if(l == r)return 1;
if(ask[l][r])return ask[l][r];
printf("%d ",r-l+1);
for(int i = l;i<=r;++i)printf("%d ",i);
fflush(stdout);
int res;
scanf("%d",&res);
return ask[l][r] = res;
}
int tot,fa[N];
int pre[N];
int find(int x){ return fa[x] == x ? x : fa[x] = find(fa[x]); }
int main(){
scanf("%d",&n);
for(int i = 1;i<=n;++i)fa[i] = i;
col[1] = ++tot;
for(int i = 2;i<=n;++i){
if(qry(1,i) == qry(1,i-1) + 1){
col[i] = ++tot;
}else{
int l = 1, r = i-1, res = i-1;
while(l <= r){
int mid = (l + r) >> 1;
if(qry(mid,i) == qry(mid,i-1) + 1){
r = mid - 1;
res = mid-1;
}else l = mid+1;
}
fa[i] = find(res);
}
}
printf("0 ");
for(int i = 1;i<=n;++i)printf("%d ",col[find(i)]);
fflush(stdout);
return 0;
}

浙公网安备 33010602011771号