标签:ch int ll P5687 一行 一列 SJX2019 include CSP
思路
首先非常简单的\(O(n^2)\)暴力很容易想,但是拿不到满分。看到最小生成树,首先就想到了\(kruscal\),但点的总数已经是\(n*m\),所以普通的过不了。发现特殊性质就是一行或者一列都是一个边权,肯定要通过这个性质来优化,但是我想了一天还是没想出来,太菜了。看了题解才发现原来是对这个算法理解不深,还是没有吃透原理。
首先\(kruscal\)的原理就是贪心,把边权排序然后一个个加边。此题也是同理,只不过将每一行和每一列当成一条边来排序然后加边。注意到如果只有列或者只有行是不可能把所有的点连起来的,所以可以先把最小的那一行和最小的那一列加入答案,然后排序模拟。但是发现还有一个难题,就是要避免环的情况,就是要避免重复计算导致答案过大。假设相邻两行和任意两列围成了一个大矩阵,然后右边的那一列和下边的一行边权较小,那么就把它们加入答案。然后考虑到一个大矩阵需要且只需要连三条边就可以联通,而如果连了左边的一行,右边的一行连起来对上下相邻两列其实已经没有意义了。因为右边的一行已经联通了上下,所以在确定了列的大小关系之后,这一行方格(被两行夹住的)就不会再用到左边的边了。所以减去1再乘上边权。本来我看不太懂别人写的题解,想自己写一篇明白的,但好像还是很迷惑,只要懂了就很简单,但是不懂是真的啥也不会。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n,m;
ll a[300005],b[300005];
ll ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++){
scanf("%lld",&b[i]);
}
sort(a+1,a+1+n);
sort(b+1,b+1+m);
ans+=a[1]*(m-1)+b[1]*(n-1);
int sum1=1,sum2=1,a1=2,b1=2;//记录已经连起来的边和列
while(a1<=n&&b1<=m){
if(a[a1]<=b[b1]){
ans+=(m-sum1)*a[a1];
a1++;
sum2++;//连起来一行,则列的贡献-1
}
else{
ans+=(n-sum2)*b[b1];
b1++;
sum1++;
}
}
printf("%lld\n",ans);
return 0;
}
标签:ch,int,ll,P5687,一行,一列,SJX2019,include,CSP 来源: https://www.cnblogs.com/57xmz/p/13713492.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。