[eJOI2018] 互素树

题目背景

本题译自 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.4110.01<�≤0.050.01<R≤0.0566
0.26<�≤0.330.26<R≤0.33220.005<�≤0.010.005<R≤0.0177
0.19<�≤0.260.19<R≤0.26330.001<�≤0.0050.001<R≤0.00588
0.12<�≤0.190.12<R≤0.19440<�≤0.0010<R≤0.00199
0.05<�≤0.120.05<R≤0.1255�=0R=01010

当 �>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),输入数据有特殊性质(如叶子结点较多,是二叉树等),且这些具有特殊性质的树在各个测试点中输入数据均匀分布。

  • 对于其他测试点,数据为随机生成。

数据范围
测试点编号12345678910
文件名01020304050607080910
�T3310010010010050050020020050501010552211
�n771010303010010050050020002000100001000020000200005000050000100000100000

附件下载

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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值