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

很简单吧。

总结

这题看着很简单,实则也不难,只要推出式子,代码就变得很简单了,总之这道题的难度不到绿,不知道怎么进的金组的。

posted @ 2026-08-22 22:38  xmgene  阅读(7)  评论(0)    收藏  举报