ABC282E Choose Two and Eat One
又一个图论的回顾——Kruskal最小(最大)生成树算法。
看到\(n\)的范围只有\(500\),应该没有什么特别的算法。那么我们考虑建一个*\(n\)个顶点的完全图,节点\(x\)到节点\(y\)的边权值就是\(x^y+y^x\)。然后跑一遍最大生成树,得到的和就是最大结果了。
如何证明生成树的边一定满足题目条件?因为一共要吃掉\(n-1\)个球,也就是删除图上的\(n-1\)条边,若想让这\(n-1\)条边连接\(n\)个点,必须要求这些边构成一个树。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct edge{
int u,v,w;
}edges[250010];
int n,m,a[510],fa[510],cnt;
bool cmp(edge a,edge b){return a.w>b.w;}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int qpow(int a,int b){
int fac=1;
while(b){
if(b&1) fac=(fac*a)%m;
a=(a*a)%m,b>>=1;
}
return fac;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
edges[++cnt].u=i,edges[cnt].v=j;
edges[cnt].w=(qpow(a[i],a[j])+qpow(a[j],a[i]))%m;
}
}
sort(edges+1,edges+1+cnt,cmp);
for(int i=1;i<=n;i++) fa[i]=i;
int edgecnt=0,ans=0;
for(int i=1;i<=cnt;i++){
int u=find(edges[i].u),v=find(edges[i].v);
if(u==v) continue;
fa[u]=v;
edgecnt++;
ans+=edges[i].w;
if(edgecnt>=n-1) break;
}
cout<<ans;
return 0;
}