[FJWC2019 Day2] 直径
时间限制:1000MS 内存限制:524288KB
难度: \(4.0\)
-
题目描述
你需要构造一棵至少有两个顶点的树,树上的每条边有一个非负整数边权。树上两点 \(i,j\) 的距离 \(dis(i,j)\) 定义为树上连接 \(i\) 和 \(j\) 这两点的简单路径上的边权和。
我们定义这棵树的直径为,所有满足 \(1 \leq i < j \leq n\) 的 \((i,j)\) 中,\(dis(i,j)\) 最大的。如果有多个这样的 \((i,j)\),那么均为直径。
作为一个构造题,你需要构造一个恰有 \(k\) 个直径的树。可以证明在给定的限制下一定有解。
-
输入格式
一行一个正整数 \(k\),表示你需要构造出一个恰有 \(k\) 个直径的树。
-
输出格式
第一行一个正整数 \(n\),表示你构造的树的点数。
接下来 \(n-1\) 行,每行三个整数 \(i,j,w\),表示一条连接点 \(i\) 和 \(j\) (点的编号为 \(1,2, \cdots, n\))的树边,边权为 \(w\) 。
-
样例输入
1 |
3 |
-
样例输出
1 2 3 4 5 |
5 1 2 2 3 2 2 2 5 3 4 2 2 |
-
样例说明
这只是一种符合题意的输出,可能还有其他输出。在这个输出中,直径为 \((1,5),(3,5),(4,5)\) 。
-
数据规模与约定
注意,你需要构造出的树必须满足 \(2 \leq n \leq 5000, 0 \leq w \leq 10^5\) !
对于 \(30pts\) 的数据,\(1\leq k \leq 2000\) ;
对于 \(100pts\) 的数据,\(1\leq k \leq 5 \times 10^6\) 。
YZOJ P4260
大部分人都当场切掉了,我没有一个清醒的思路就没有构造出来。。。
首先,样例是一个菊花。可以很容易发现,要构造出 \(k\) 条直径,只需要按照下图的方案:
这样就是用 \(k+2\) 个点构造出一个菊花,显然有 \(k\) 条最长链,拿到 \(30pts\) 。
因为 \(2 \leq n \leq 5000\) ,\(k\) 很大,所以考虑将 \(k\) 进行分解。我当时就想到可以往点外连 \(a\) 条边权为 \(1\) , \(b\) 条边权为 \(2\) 形成的菊花,这样答案就是 \(k=a \times b\) ,可以把 \(k\) 变小。但是这样做的话万一 \(k\) 是个大质数,那又完蛋了,没有办法分解。
然而到这里我就想不下去了。其实离正解只差一步之遥:\(k\) 分解的还不够。
考虑向外连三个菊花(如下图),这样 \(k=ab+ac+bc\) 。
这样子就有办法分解了!
\(k=ab+ac+bc=a(b+c)+bc\) ,即 \(a=\frac{k-bc}{b+c}\) 。由于 \(a+b+c \leq n-4\) ,所以可以直接暴力枚举 \(b, c\) 然后算出合法的 \(a\) ,这样就得到了一组合法的构造方案了,时间复杂度 \(O(n^2)\) 。
此外,本题还可以利用边权为 \(0\) 的性质挂链,或者其他一些奇奇怪怪的做法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #define MXN 5000 int main() { //freopen("diameter.in","r",stdin);freopen("diameter.out","w",stdout); int k;scanf("%d",&k); for(register int b=1;b<=MXN-4;b++) for(register int c=1;b+c<=MXN-4;c++) if(k>=b*c && (k-b*c)%(b+c)==0) { register int a=(k-b*c)/(b+c); if(a+b+c>MXN-4) continue; printf("%d\n",a+b+c+4); printf("%d %d %d\n",1,2,1); printf("%d %d %d\n",1,3,1); printf("%d %d %d\n",1,4,1); register int l=4; for(register int i=1;i<=a;i++) printf("%d %d %d\n",2,++l,1); for(register int i=1;i<=b;i++) printf("%d %d %d\n",3,++l,1); for(register int i=1;i<=c;i++) printf("%d %d %d\n",4,++l,1); return 0; } return 0; } |