题目大意
给出
n
n
n个结点,编号为
i
i
i的结点的权值为
F
i
b
i
Fib_i
Fibi,斐波拉契数列的第
i
i
i项。
一条连接
x
,
y
x,y
x,y的无向边的边权为
x
,
y
x,y
x,y的点权之和。
求最小生成树,且要求这棵树的最大度数最小。
时间限制
1s
数据范围
n ≤ 1 0 5 , m ≤ 2 × 1 0 5 n\le 10^5,m\le 2\times 10^5 n≤105,m≤2×105
题解
这里的边权十分之大,完全不可能直接计算出来,
此时就需要用到斐波拉契数列的一些性质了。
可以发现就是任意两点的点权之差是十分巨大的,并不是在加上一项就能够弥补的。
因此就可以用双关键字排序,而无需将所有边权都计算出来之后再排序。
对于度数的要求,可以看出来,边权相同是不可能的,因此最小生成树只有一种形态。
Code
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <map>
#define ll long long
#define G getchar
using namespace std;
ll read()
{
char ch;
for(ch = G();ch < '0' || ch > '9';ch = G());
ll n = 0;
for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
return n;
}
void write(ll x)
{
if (x > 9)
{
write(x / 10);
putchar(x % 10 + 48);
}
else putchar(x + 48);
}
const int N = 100005;
int f[N] , d[N] , ans;
int n , m , x , y;
struct node
{
int x , y;
bool operator < (const node &n) const
{
return y < n.y || (y == n.y && x < n.x);
}
}a[N * 2];
int get (int x)
{
if (f[x] == x) return x; else f[x] = get(f[x]);
return f[x];
}
int main()
{
//freopen("i.in","r",stdin);
//freopen("e.out","w",stdout);
n = read();
m = read();
for (int i = 0 ; i < m ; i++)
{
x = read();
y = read();
if (x > y) swap(x , y);
a[i].x = x;
a[i].y = y;
}
sort(a , a + m);
memset(d , 0 , sizeof(d));
for (int i = 1 ; i <= n ; i++)
f[i] = i;
for (int i = 0 ; i < m ; i++)
{
x = get(a[i].x);
y = get(a[i].y);
if (x != y)
{
d[a[i].x]++;
d[a[i].y]++;
f[x] = y;
}
}
ans = 0;
for (int i = 1 ; i <= n ; i++)
ans = max(ans , d[i]);
printf("%d\n", ans);
return 0;
}