题目背景
本题译自 eJOI2018 Problem E「Prime Tree」
题目描述
本题为提交答案,输入文件在附件中。
设有一棵有 �n 个结点的树,其结点编号为 11 到 �n 。
对于其中的任意一条边 (�,�)(u,v) ,如果存在一个正整数 �>1d>1 满足 �∣�,�∣�d∣u,d∣v ,我们称它为一条坏的边。
下图中的树有三条坏的边—— (6,4)(6,4)(都被 22 整除), (2,6)(2,6)(都被 22 整除), (3,6)(3,6)(都被 33 整除)。
你的任务是将结点重新编号,使得图中坏的边的数量尽量少。
对于上图中的树,按照下图中的方式将结点重新编号,会只剩一条坏的边 (3,6)(3,6) 。
重新编号后,坏的边越少,你的得分越高。
这是一道提交答案题。你应当下载输出文件,然后在本地运行你的程序,将输出结果上传。当然,在洛谷上你可以直接提交你的程序。
输入格式
每个测试点中有多组测试数据。
第一行,一个整数 �T,表示测试数据组数。
每组测试数据共 �n 行,其中 �n 表示树的结点个数。
第一行,一个整数 �n;
接下来 �−1n−1 行,每行两个整数 �u 和 � (1≤�,�≤�)v (1≤u,v≤n),表示一条边 (�,�)(u,v) 。
每个输入文件中,每棵树的结点个数相同。
输出格式
对于每组测试数据,输出一行 �n 个整数,表示原先编号为 11 到 �n 的结点的新编号。
每个结点的编号必须不同,也就是说同一组测试数据中输出的 �n 个整数必须互不相同。
输入输出样例
输入 #1复制
2 6 1 3 3 5 3 6 6 4 6 2 6 1 2 1 3 1 4 1 5 1 6
输出 #1复制
2 5 3 1 4 6 5 1 2 3 4 6 4 5 1 3 6 2 5 4 6 1 3 2
说明/提示
计分方式
对于每个测试点,设所有树的总边数为 �=�×(�−1)M=T×(n−1),你的输出中坏的边数为 �X,记 �=��R=MX ,你的得分与 �R 的关系如下:
�R | 得分 | �R | 得分 |
---|---|---|---|
0.33<�≤0.40.33<R≤0.4 | 11 | 0.01<�≤0.050.01<R≤0.05 | 66 |
0.26<�≤0.330.26<R≤0.33 | 22 | 0.005<�≤0.010.005<R≤0.01 | 77 |
0.19<�≤0.260.19<R≤0.26 | 33 | 0.001<�≤0.0050.001<R≤0.005 | 88 |
0.12<�≤0.190.12<R≤0.19 | 44 | 0<�≤0.0010<R≤0.001 | 99 |
0.05<�≤0.120.05<R≤0.12 | 55 | �=0R=0 | 1010 |
当 �>0.4R>0.4 时,不能得分。
对于所有的测试点,保证存在 �=0X=0 的输出。
样例解释
注意样例中给出了两种合法的输出,为了方便,下称输出 1 和 输出 2。
第一组测试数据已经在题目描述中讨论过。输出 1 中有一条坏的边 (3,6)(3,6),输出 2 中没有坏的边。
第二组测试数据中,输出 1 和输出 2 都没有出现坏的边。
样例中,�=2×5=10M=2×5=10。
对于输出 1,�=1,�=110=0.1X=1,R=101=0.1,该输出将得到 55 分。
对于输出 2,�=0,�=010=0X=0,R=100=0,该输出将得到 1010 分。
数据范围和限制
数据限制
- 对于测试点 1 (输入
01
),�=3,�=7T=3,n=7,三棵树分别如下所示:
-
对于测试点 4 至 8 (输入
04
至08
),输入数据有特殊性质(如叶子结点较多,是二叉树等),且这些具有特殊性质的树在各个测试点中输入数据均匀分布。 -
对于其他测试点,数据为随机生成。
数据范围
测试点编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
文件名 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
�T | 33 | 100100 | 100100 | 500500 | 200200 | 5050 | 1010 | 55 | 22 | 11 |
�n | 77 | 1010 | 3030 | 100100 | 500500 | 20002000 | 1000010000 | 2000020000 | 5000050000 | 100000100000 |
附件下载
PrimeTree.zip2.65MB
代码如下
#include <bits/stdc++.h>
using namespace std;
#define N 100005
#define pb push_back
#define set(a,vl) memset(a,vl,sizeof(a))
int T,n,ord[N],a[N];bool col[N];set<int> z;
vector<int> vc1[2],e[N];
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
void dfs(int u,int f)
{
col[u]=col[f]^1;vc1[col[u]].pb(u);
for(int i=0,v;i<e[u].size();++i)
{v=e[u][i];if(v!=f) dfs(v,u);}
}
bool chk(int u,int x)
{
for(int i=0,v;i<e[u].size();++i)
{v=e[u][i];if(a[v] && gcd(x,a[v])>1) return 0;}return 1;
}
void upd(int u,int x) {a[u]=x;z.erase(u);}
bool slv1()
{
ord[0]=0;set(a,0);z.clear();
random_shuffle(vc1[0].begin(),vc1[0].end());
for(int i=2;i<=n;i+=2) a[vc1[0][i/2-1]]=i;
for(int i=1;i<=n;i+=2) ord[++ord[0]]=i;
random_shuffle(ord+1,ord+ord[0]+1);
for(int i=1;i<=n;++i) if(!a[i]) z.insert(i);
for(int i=1,j;i<=ord[0];++i)
{
bool fl=0;
for(set<int>::iterator it=z.begin();it!=z.end();++it)
{j=*it;if(chk(j,ord[i])) {upd(j,ord[i]);fl=1;break;}}
if(!fl) return 0;
}return 1;
}
void slv()
{
scanf("%d",&n);vc1[0].clear();vc1[1].clear();
for(int i=1;i<=n;++i) e[i].clear();
for(int i=1,u,v;i<n;++i)
scanf("%d %d",&u,&v),e[u].pb(v),e[v].pb(u);dfs(1,0);
if(vc1[0].size()<vc1[1].size()) swap(vc1[0],vc1[1]);
while(1) if(slv1()) break;
for(int i=1;i<=n;++i) printf("%d ",a[i]);puts("");
}
int main()
{
srand(time(0));
scanf("%d",&T);while(T--) slv();return 0;
}