题意:
求最小生成树。(
n
2
n^2
n2条边:需要自己算一共有
n
n
n个点的坐标)
不过你可以选择一个套餐,花费
c
c
c使得套餐内点全部连通。
套餐数量
q
≤
8
,
n
≤
1000
q≤8,n≤1000
q≤8,n≤1000
题解:
套餐数量较少,我们可以枚举(二进制即可)。
枚举之后把所有包含的边权赋值为
0
0
0
求最小生成树并且能够使得(边权赋值为
0
0
0的),
p
r
i
m
prim
prim不太好实现。
思路一:
枚举
∗
(
*(
∗(排序新边+
k
r
u
s
k
a
l
)
kruskal)
kruskal)
显然复杂度为
2
8
∗
(
8
∗
n
+
2
n
2
l
o
g
2
n
+
n
2
)
≈
1
0
8
2^8*(8*n+2n^2log_2n+n^2)≈10^8
28∗(8∗n+2n2log2n+n2)≈108朝上
思路二:
我们考虑免费套餐之后,其实我们只需要判连通即可,对枚举选中的免费套餐里的点连通,
再跑最小生成树,当所有点连通就退出。
但是这样的复杂度也是
2
8
∗
(
8
∗
n
+
n
2
+
n
l
o
g
n
)
+
2
n
2
l
o
g
n
2^8*(8*n+n^2+nlogn)+2n^2logn
28∗(8∗n+n2+nlogn)+2n2logn,常数小了很多但是还是不行。
主要在于每次求最小生成树用了
n
2
n^2
n2条边(最多,但是如果有一个点特别远,就会卡到
n
2
n^2
n2)
思路三:
我们考虑先做出一个最小生成树,对于上面的边能够使得所有点连通,我们每次对套餐内的点连通后,至多处理
n
−
1
n-1
n−1条边(如果这条边无法影响就不加上价值)。再往后遍历也是没有意义的。(因为如果较大的边连接的点在套餐里,那么已经被选择过了,如果不在:那为什么不选前面的边呢。
复杂度:
2
8
(
8
∗
n
+
n
−
1
)
=
1
e
6
2^8(8*n+n-1)=1e^6
28(8∗n+n−1)=1e6,因为
2
∗
9
=
18
2*9=18
2∗9=18
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<cctype>
#include<string>
#include<cmath>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define eps 1e-6
const int maxn = 10010;
int n,q,cnt=0;
int f[1010];ll value[10];
ll d[1010][1010];
pair<ll,ll>pos[1010];
vector<int>G[10],tree;
int find(int x){return f[x]==x?x:(f[x]=find(f[x]));}
void uni(int x,int y){f[find(x)]=find(y);}
bool query(int x,int y){return find(x)==find(y);}
struct node{
int x,y;ll dist;
friend bool operator < (node a,node b){
return a.dist<b.dist;
}
}A[1000010];
void init(){
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
d[j][i]=d[i][j]=(pos[i].first-pos[j].first)*(pos[i].first-pos[j].first)+(pos[i].second-pos[j].second)*(pos[i].second-pos[j].second);
}
}
cnt=0;
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
A[++cnt]=node{i,j,d[i][j]};
}
}
sort(A+1,A+1+cnt);
tree.clear();
for(int i=1;i<=n;i++)f[i]=i;
int now=n;
for(int i=1;i<=cnt;i++){
if(!query(A[i].x,A[i].y)){
tree.push_back(i);
uni(A[i].x,A[i].y);
now--;
if(now==1)break;
}
}
}
int main(){
int T;scanf("%d",&T);
for(int kase=1;kase<=T;kase++){
scanf("%d%d",&n,&q);
FOR(i,1,q){
int num;scanf("%d%lld",&num,&value[i]);
G[i].clear();
FOR(j,1,num){
int x;scanf("%d",&x);
G[i].push_back(x);
}
}
FOR(i,1,n){
scanf("%lld%lld",&pos[i].first,&pos[i].second);
}
init();
ll ans=0x3f3f3f3f3f3f3f3f;
for(int i=0;i<(1<<q);i++){
for(int j=1;j<=n;j++)f[j]=j;
ll res=0;
for(int j=1;j<=q;j++){
int x=(1<<(j-1));
if(i&x){
res+=value[j];
for(int k=0;k<G[j].size()-1;k++){
uni(G[j][k],G[j][k+1]);
}
}
}
for(int j=0;j<tree.size();j++){
int u=tree[j];
if(!query(A[u].x,A[u].y)){
uni(A[u].x,A[u].y);
res+=A[u].dist;
}
}
ans=min(res,ans);
}
cout<<ans<<endl;if(kase<T)puts("");
}
}