题解:P17740 [ICPC 2019 Xuzhou R] Loli, Yen-Jen, and a graph problem
songzirui_ · · 题解
SOLUTION
删除了原题中不和谐的内容。
参观同机房大佬做法,发现我们的做法非常丑陋,但是也是对的,遂写题解。
本题做法由 @songzirui_ 与 @OIer_sundingjia 共同得出。
首先我们需要知道迹实际上就是路径,而且没有要求是简单路径,也就是说可以重复经过一个点。
考虑
那我们求出欧拉回路,再把回路划分成
对于
具体地,把所有点两两配对。
不妨设一对点中左边的是
尝试对每个
对
于是我们得到了迹长在
这启发我们把所有点八个一组分类。
对于
观察这个图形,可以发现它把从
需要注意的一点是,并不能直接使用这个图形当作长度为
发现在第二张图中,每一条边都是平着连,或者隔一层连的,跨度不超过一层。
于是只要保证,第三张图的跨度大于一层,即可保证两张图所连接的边没有冲突。
此处证明读者感性理解即可,不再给出严格表示。
对于
这样我们就成功归纳出了一组操作,将已经连的边全部删除,使得原图上所有点的度数都是偶的。
对于
否则只要原图连通,那一定有一条欧拉回路,于是只要证明原图依然连通。
显然
据第二张图,有
对于
最后求出欧拉路径,划分剩下的迹就做完了。
实现见代码。
CODE
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fi first
#define se second
int T,n,m;
vector<int> vec[1010];
int tot=0;
int dis[1010][1010];//dis->disabled,dis[i][j]=1 代表i-j边已经被使用。
void link(int i,int j){
dis[i][j]=dis[j][i]=1;
vec[tot].push_back(j);
}
// @OIer_sundingjia 认为图二所示像是蝴蝶,遂命名。
void butterfly(int i,int j){
link(i,j+1),link(j+1,i+1),link(i+1,j),link(j,i);
}
stack<int> stk;
int st[1010];//当前弧优化
void dfs(int u){
for(int i=st[u]+1;i<=n;i++){
if(i==u) continue;
if(!dis[u][i]){
dis[u][i]=dis[i][u]=1;
dfs(i);
}
st[u]=i;
}
stk.push(u);
}
vector<int> e;//euler_path
void solve(){
while(stk.size()){
e.push_back(stk.top()),stk.pop();
}
int c=0;
while(1){
tot++;
if(tot==n) break;
vec[tot].push_back(e[c]);
for(int i=c;i<c+tot;i++){
link(e[i],e[i+1]);
}
c+=tot;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
if(n==4){//corner case,因为这个吃了两发罚时
cout<<"1 2\n1 3 4\n1 4 2 3\n";
return 0;
}
if(n%2==0){
for(int i=1;i<=n;i+=2){
int c=i/8;
++tot;
vec[tot].push_back(i);
for(int j=1;j<=c;j++){
butterfly(i,i-2*(j+1));
}
if(i%8==1){
link(i,i+1);
}
else if(i%8==3){
link(i,i-2),link(i-2,i+1);
}
else if(i%8==5){
link(i,i-2),link(i-2,i-1),link(i-1,i+1);
}
else{
link(i,i+1),link(i+1,i-1),link(i-1,i-2),link(i-2,i+1);
}//大分讨
}
}
dfs(1);
solve();
for(int i=1;i<n;i++){
for(int v:vec[i]){
cout<<v<<' ';
}
cout<<'\n';
}
return 0;
}