洛谷 P5425 [USACO19OPEN] I Would Walk 500 Miles G
题目描述
Farmer John 想要将他的编号为 $ 1 \ldots N $ 的 $ N $ 头奶牛( $ N \leq 7500 $ )分为非空的 $ K $ 组( $ 2 \leq K \leq N $ ),使得任意两头来自不同组的奶牛都需要走一定的距离才能相遇。奶牛 $ x $ 和奶牛 $ y $ (其中 $ 1 \leq x<y \leq N $ )愿意为了见面走 $ (2019201913x+2019201949y) \mod 2019201997 $ 英里。
给定一个将 $ N $ 头奶牛分为 $ K $ 个非空小组的分组方案,令 $ M $ 为任意两头来自不同组的奶牛愿意为了见面行走的英里数的最小值。为了测试奶牛们相互之间的忠诚度,Farmer John 想要将 $ N $ 头奶牛以最佳的方式分为 $ K $ 组,使得 $ M $ 尽可能大。
输入格式
输入仅有一行,包含 $ N $ 和 $ K $ ,用空格分隔。
输出格式
输出最优的 $ M $ 。
输入输出样例 #1
输入 #1
3 2
输出 #1
2019201769
说明/提示
在这个例子中,奶牛 \(1\) 和奶牛 \(2\) 愿意为了见面走 \(2019201817\) 英里。奶牛 \(2\) 和奶牛 \(3\) 愿意走 \(2019201685\) 英里。奶牛 \(1\) 和奶牛 \(3\) 愿意走 \(2019201769\) 英里。所以,将奶牛 \(1\) 单独分为一组,奶牛 \(2\) 和奶牛 \(3\) 分为一组,\(M = \min(2019201817, 2019201769) = 2019201769\)(这是我们在这个问题中能够达到的最佳结果)。
算法分析
如果直接暴力跑 \(kruskal\)的话,那么时间复杂度最坏是\(O(n^2logn^2)\),会TLE,所以想到优化。一种可行的方案是用贪心,假设现在\(n\)为7500,那么7500和7499连边肯定是最优的,接着是7499和7498......,所以到\(x=k-1,y=n\)的时候就停止了,所以答案就是把\(x=k-1,y=n\)代进式子,算出答案即可。
暴力代码
#include<bits/stdc++.h>
using namespace std;
//题目中的常数
const long long t1 = 2019201913;
const long long t2 = 2019201949;
const int mod = 2019201997;
struct node{
int x,y,z;
};
bool cmp(node a,node b){//排序规则
return a.z<b.z;
}
int n,m;
int k = 0;
int fa[7505];
node a[56250005];//n^2条边
int find(int x){
if(fa[x]==x){
return x;
}else{
return fa[x] = find(fa[x]);
}
}
void kruskal(){
int cnt = n;//n个连通块
sort(a+1,a+1+k,cmp);//排序
for(int i = 1; i<=k; i++){
int x = find(a[i].x);
int y = find(a[i].y);
if(x!=y){
fa[x] = y;//合并
cnt--;
if(cnt==m-1){//超出限定
cout<<a[i].z;//输出答案
return ;
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i = 1; i<=n; i++){
fa[i] = i;//初始化
for(int j = i+1; j<=n; j++){
long long t = (t1*i+t2*j)%mod;//算出权值
a[++k] = node{i,j,(int)t};//建边
}
}
kruskal();
return 0;
}
提醒一句,千万不要#define int long long,不然会MLE,多错一个测试点。
AC代码
简短的代码,只要推出式子,代码很简单。
#include<bits/stdc++.h>
using namespace std;
//题目中的常数
const long long t1 = 2019201913;
const long long t2 = 2019201949;
const int mod = 2019201997;
long long n,k;
int main(){
cin>>n>>k;
cout<<(t1*(k-1)+t2*n)%mod;//代入式子
return 0;
}
很简单吧。
总结
这题看着很简单,实则也不难,只要推出式子,代码就变得很简单了,总之这道题的难度不到绿,不知道怎么进的金组的。

浙公网安备 33010602011771号