Solution-P8921
D 验题人 sol
这里提供一种代码更短的
Update:被当做官方解法拿去讲了,实际上本质相同。
Update:修正一个 typo,添加 Python 程序。
背景
实际上这场比赛的备赛周期非常长。这道题在 8 月就出出来了,当时我就睡觉的时候把这题口胡了,第二天和 JV 讲了,他表示这做法好想好写,觉得这题要被橄榄。但是后来咕咕咕了。再接着到了 12 月。
然后我就退役了。但是一想到这题可能是我在役期间唯一独立完成的 div2D(而且和出题人做法完全不同),我逼自己完成了这个实现,不能让这个奇妙想法就此埋没。
JV 很喜欢三角剖分啊。上次那个 You are the Miserable 也是三角剖分的性质。
做法
当
当
如果我们在原来基础上连续添加一对相邻三角形(左图,增加
接下来需要证明所有
如果我们以每个三角形为结点,相邻三角形间连边,那么每个点度数不超过
如果我们要给树
- 如果
T 的两个孩子都有偶数个三角形,则分别让两个孩子自给自足即可,根等待父亲连边。 - 如果
T 两个孩子都有奇数个三角形,则我们用上面右图的方法连接两个孩子。 - 如果
T 两个孩子一奇一偶,则根应该和奇数子树的根使用左图方法连接。
这样整棵树
实现
对偶图,听上去好办,实操起来细节很多。
第一个问题是如何给
我们可以采用计数排序,但是如果
但是本题因为排序是为了求环上的前驱后继,所以对序列
同时为了方便查找我们可以放 vector 中的下标。
第二个问题是如何区分三角形的三条边。
这个建议大家边在草稿纸上画图边写,位置关系要和草稿纸严格对应。
我草稿纸上的图 dfs(u,v,w) 中,
写的时候还是脑抽很多次了的。比如说,两次 DFS(一次求子树大小,一次染色)不会带来任何 coding 的方便,徒增常数,直到我 AC 了这道题我才意识到。参考代码如下:
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> g[300005],ng[300005];
unordered_map<int,int> loc[300005];
#define lc g[u][loc[w][u]+1]
#define rc g[v][loc[w][v]-1]
int tree(int u,int v,int w)//返回值为子树大小
{
int lsz=0,rsz=0;
if(not(w==u-1 or w==n and u==1))lsz=tree(u,w,lc);
if(not(w==v+1 or w==1 and v==n))rsz=tree(w,v,rc);
if((lsz+rsz)%2)//如果子树大小为偶数
if(lsz%2==1)
printf("%d %d\n%d %d\n",u,w,u,lc);
else
printf("%d %d\n%d %d\n",v,w,v,rc);
else if(lsz%2==1)//如果两个奇数那么将两个孩子连起来
printf("%d %d\n%d %d\n",w,lc,w,rc);
return lsz+rsz+1;
}
int main()
{
scanf("%d",&n);
if(n%2){puts("-1");return 0;}
int u,v;
for(int i=1;i<=n-3;i++)
{
scanf("%d%d",&u,&v);
ng[u].push_back(v);
ng[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
ng[i].push_back(i%n+1);
ng[i%n+1].push_back(i);
}
for(int i=1;i<=n;i++)//问题 1:计数排序,i 枚举值
for(int j:ng[i])
{
if(j>i)continue;//j<i 的边在前
loc[i][j]=g[j].size();
g[j].push_back(i);
}
for(int i=1;i<=n;i++)
for(int j:ng[i])
{
if(j<i)continue;//j>i 的边在后
loc[i][j]=g[j].size();
g[j].push_back(i);
}
printf("1 2\n");//一开始将 1,2 加入答案
tree(1,2,g[1][1]);//可以保证这个三角形一定是贴边的,构成二叉树
return 0;
}
Python 3 解法(虽然常数比较大,无法通过 Subtasks 3 and 6):
n=int(input())
import sys
sys.setrecursionlimit(300005)
if n%2:
print(-1)
sys.exit(0)
ng,g=[[] for i in range(n+5)],[[] for i in range(n+5)]
loc=[{} for i in range(n+5)]
def tree(u,v,w):
lsz,rsz=0,0
if not(w==u-1 or w==n and u==1):
lc=g[u][loc[w][u]+1]
lsz=tree(u,w,lc)
if not(w==v+1 or w==1 and v==n):
rc=g[v][loc[w][v]-1]
rsz=tree(w,v,rc)
if (lsz+rsz)%2:
if lsz%2:
print("%d %d\n%d %d"%(u,w,u,lc))
else:
print("%d %d\n%d %d"%(v,w,v,rc))
elif lsz%2==1:
print("%d %d\n%d %d"%(w,lc,w,rc))
return lsz+rsz+1
for i in range(n-3):
u,v=map(int,input().split())
ng[u].append(v)
ng[v].append(u)
for i in range(1,n+1):
ng[i].append(i%n+1)
ng[i%n+1].append(i)
for i in range(1,n+1):
for j in ng[i]:
if j<i:
loc[i][j]=len(g[j])
g[j].append(i)
for i in range(1,n+1):
for j in ng[i]:
if j>i:
loc[i][j]=len(g[j])
g[j].append(i)
print(1,2)
tree(1,2,g[1][1])