吐槽:这种标题也是没谁了
题目描述
省府弟子乐天潭在条头日今的笔试中遇到了四道题,其中第一题是:
已知 n 和 t,构造一棵 n 个结点的树,使得边权和最大。
结点的编号为 1 到 n,对于边权 w(u,v) = GCD(u,v) &t。
乐天潭很强势,一眼就知道了解法,但计算量实在过大,没办法只好偷偷掏出手机请你帮他写一个程序算出答案。
输入
第一行,两个整数 n (1 <= n <= 3000),t (1 <= t <= 10^9+7)。
输出
输出一行,一个整数,表示答案。
输入样例
3 3
输出样例
2
提示
w(u,v)表示边(u,v)的边权,GCD(u,v)表示u和v的最小公约数,&表示二进制按位与
样例有三种构造方法:
方法一:将1和2,2和3连边,GCD(1,2)&3 + GCD(2,3)&3 = 2
方法二:将1和2,1和3连边,GCD(1,2)&3 + GCD(1,3)&3 = 2
方法三:将1和3,2和3连边,GCD(1,3)&3 + GCD(2,3)&3 = 2
题解:最大生成树,边权就是gcd(u,v)&t,由于边数太多,请使用坑爹的prim算法。。。
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define pa pair<long long , int >
#define LiangJiaJun main
#define INF 199912270000LL
using namespace std;
int n;long long t;
long long gcd(long long x,long long y){
return y==0?x:gcd(y,x%y);
}
priority_queue<pa,vector<pa>,greater<pa> >q;
int mp[3004][3004];
long long mcs[3004];
bool vis[3004];
long long prim(){
for(int i=0;i<=n;i++)mcs[i]=INF;
long long ans=0;
q.push(make_pair(0,1));
mcs[1]=0;
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;
ans+=mcs[x];vis[x]=1;
for(int i=1;i<=n;i++){
if(mcs[i]>mp[x][i]&&x!=i){
mcs[i]=mp[x][i];
q.push(make_pair(mcs[i],i));
}
}
}
return -ans;
}
int LiangJiaJun(){
scanf("%d%I64d",&n,&t);
for(int i=1;i<=n;i++){
mp[i][i]=0;
for(int j=1;j<i;j++){
mp[j][i]=mp[i][j]=-(gcd((long long)i,(long long)j)&t);
}
}
printf("%d\n",prim());
return 0;
}