专栏文章
题解:AT_arc152_d [ARC152D] Halftree
AT_arc152_d题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mir023rt
- 此快照首次捕获于
- 2025/12/04 13:32 3 个月前
- 此快照最后确认于
- 2025/12/04 13:32 3 个月前
题意
给定 ,有一个 个点的图,点编号为 .图初始为空,你需要做若干次操作,使得图变为一颗树,或说明无解。
操作:选择两个数 ,连边 ,。
,。
做法
个点的数有 条边,而操作每次都会加两条边,所以当 为偶数时无解。下记 。
当 为奇数时,考虑将 的边加在图上会发生什么。当 时,裴蜀定理告诉我们所有点都在同一个环上;当 时, 对模 的余数不会变,它会形成 个环。
把他排得好看一点,比如像下面这样:

这是 的情况。
首先,这样的图一定有奇数个环和奇数条射线(由于 )是奇数。在这样的图上,操作相当于连一条边,将它绕中心顺时针旋转 ,然后再连旋转后的边。下面把选的边 标红, 标蓝。
先把大部分射线上的所有点连起来:

由于射线的个数是奇数,这样一定会剩下一条。考虑如何把这些链连起来:

注意到,这里剩下的那一条射线的第一个点被我们接入树里了。接下来我们还剩偶数个点。像这样构造:

到这里我们就完成了 的构造。把它加以推广就可以对于任何 都构造出来了。
代码
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,d;
vector<array<int,2> >p;
signed main(){
cin>>n>>k;
if(n%2==0) return puts("-1"),0;//唯一的无解情况
d=__gcd(n,k);
printf("%lld\n",n/2);
auto add=[&](int x,int y){p.push_back({x,y});};//add(x,y)表示选择 (x,y)
for(int j=0;j<n/d-1;j+=2) add(j*k%n,(j+n-1)*k%n);//第一个环
for(int i=1;i<d;i++){
for(int j=0;j<n/d-1;j+=2) add((j*k+i-1)%n,(j*k+i)%n);//连接第 i-1 个环和第 i 个环
if(i&1) add((i-k-k+n+n)%n,(i-k+n+1)%n);//最后一步的构造
}
for(auto i:p) printf("%lld %lld\n",i[0],i[1]);
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...