FJWC2019 D2T1 直径 题解

题目来源

181.「2019冬令营提高组」直径

题目大意

构造一棵树,使直径数量为 k k k

输出构造出的树的节点数量和每条边的端点与权值。

比赛情况

比赛时,我想到正解并实现出来。

但是输出的时候把所有的节点数输成边数了,这样的会使节点数都少 1 1 1

后面也没有管理好时间没有去检查这一道题,导致直接 W a − 0 Wa-0 Wa0 100 100 100分都付之东流了。

以后做题以后用样例检查时不能仅仅只检查局部的正确性,整个答案是否合法也需检查判断。

还有比赛时不能浪费一分一秒,要是把放空那10分钟拿来检查代码,那么就不会发生这样的情况。

详细思路

对于边等长的菊花图,边的数量 a a a,这样的图的直径数量是 a ( a − 1 ) 2 \frac{a(a-1)}{2} 2a(a1)

假若菊花图中有一条边比其它边 长的话,边的数量 a a a,那么直径数量是 a − 1 a-1 a1

这样构图能够弄出每一种直径数量,但是这样构图的节点数远远大于题目限制数 5000 5000 5000

那假若不仅仅有一条比较长边呢?那么这几条相同的边会互相形成更长的直径。

但也是能避免较长边互相形成更长的直径,只要较长边与短边中间加一条非常大的边即可(如下图)

在这里插入图片描述

这样可以将图的直径数处理成两个部分的边数乘积,大幅度减少了图的节点数量

即使这样优化,遇到质数的时候,是会比第一种情况更劣

那么是否可以再分为多个部分呢?

假若分为3个部分,每个部分里的边全是相同的,不同部分的边是不同大小,那么只有最大和次大部分能产生贡献

当每个部分的边的大小相同的话,此时每两个部分间能互相产生贡献(如下图)

在这里插入图片描述

令每部分里的边的数量分别为 a , b , c a,b,c a,b,c

直径数量即为 ( a b + b c + c a ) (ab+bc+ca) (ab+bc+ca)

当直径数量比较大时,一定能够用 ( a b + b c + c a ) (ab+bc+ca) (ab+bc+ca)表示

对于求 a , b , c a,b,c a,b,c可以从 直径数量 \sqrt{直径数量} 直径数量 开始往 1 1 1枚举 a , b a,b a,b,再用 ( 直径数量 − a b ) m o d ( a + b ) = = 0 (直径数量-ab)mod(a+b)==0 (直径数量ab)mod(a+b)==0来判断合法性,合法时 c = 直径数量 − a b a + b c=\frac{直径数量-ab}{a+b} c=a+b直径数量ab

若不能用 ( a b + b c + c a ) (ab+bc+ca) (ab+bc+ca)表示,那就用第一种方法和第二种方法解决

心声

如果你仔细观察会发现,我这篇博客里所有的较大边(包括代码里的)

因为我想祝愿你,从今以后你做所有的题目都能拿到 100 100 100%的分数

具体实现
#include<cstdio>
#include<cmath>
using namespace std;
long long n,m,k=0,w,A1,A2,A3;
void Dfs(){
    for(int i=w;i>=1;i--){
        for(int j=w;j>=1;j--){
            if((n-i*j)%(i+j)==0){A1=i;A2=j;A3=(n-i*j)/(i+j);return;}
        }
    }
    return;
}
int main(){
    freopen("diameter.in","r",stdin);
    freopen("diameter.out","w",stdout);
    scanf("%lld",&n);w=sqrt(n);
    if(n==1){printf("2\n1 2 1");return 0;}
    if(n==2){printf("4\n1 2 2\n1 3 1\n1 4 1\n");return 0;}
    Dfs();
    if(A1==0&&A2==0&&A3==0){
        m=2;
        printf("%lld\n1 2 100\n",n+2);
        for(long long i=1;i<=n;i++)printf("1 %lld 1\n",i+m);
    }
    else if(A3==0){
        m=2;
        printf("%lld\n1 2 100\n",A1+A2+2);
        for(int i=1;i<=A1;i++)m++,printf("1 %lld 1\n",m);
        for(int i=1;i<=A2;i++)m++,printf("2 %lld 1\n",m);
    }
    else{
        m=4;
        printf("%lld\n1 2 100\n1 3 100\n1 4 100\n",A1+A2+A3+4);
        for(int i=1;i<=A1;i++)m++,printf("2 %lld 1\n",m);
        for(int i=1;i<=A2;i++)m++,printf("3 %lld 1\n",m);
        for(int i=1;i<=A3;i++)m++,printf("4 %lld 1\n",m);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值