Portal HDU - 3938
考察:最小生成树+并查集的运用+离线处理
永远不会动脑思考TAT
看大佬来的思路:
- 这道题的询问量很大.因此可以采取一些技巧.我们可以发现L大的答案是可以由L小的答案累加得来的.因此这道题对询问进行排序.先处理L小的询问.再进行累加推导L大的询问.
- 当i,j有多种路径,我们是选择一条经过路径带权值最大值里最小的.也就是说为了让i,j连通.我们必须选择权值小的边使得他们连通.这里可以可以考虑最小生成树的算法
- 综上所述,我们需要对询问与路径进行排序.当两点不在一个集合里时,说明这两条边的答案没有算过.经过这条边的答案为sz[i]*sz[j].这里是排列组合
- 如果两点已经在集合里,那么就说明经过这两点的答案前面已经算过了
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 const int N = 10010,M = 50010; 6 int p[N],n,m,q,sz[N]; 7 struct Edge{ 8 int u,v,w; 9 bool operator<(const Edge& x)const{ 10 return this->w<x.w; 11 } 12 }edge[M]; 13 struct Query{ 14 int id,ans,w; 15 bool operator<(const Query& x)const{ 16 return this->w<x.w; 17 } 18 }query[N]; 19 struct cmp{ 20 bool operator()(Query q1,Query q2){ 21 return q1.id<q2.id; 22 } 23 }; 24 int findf(int x) 25 { 26 if(x!=p[x]) 27 { 28 int t = findf(p[x]); 29 sz[x]+=sz[p[x]]; 30 p[x] = t; 31 } 32 return p[x]; 33 } 34 int main() 35 { 36 // freopen("in.txt","r",stdin); 37 while(scanf("%d%d%d",&n,&m,&q)!=EOF) 38 { 39 int cnt = 1; fill(sz,sz+N,1); 40 for(int i=1;i<=n;i++) p[i] = i; 41 for(int i=1;i<=m;i++) 42 scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); 43 for(int i=1;i<=q;i++) { 44 int s; 45 scanf("%d",&s); 46 query[i] = {i,0,s}; 47 } 48 sort(edge+1,edge+m+1); 49 sort(query+1,query+q+1); 50 for(int i=1;i<=q;i++)//从小到大处理查询.大的答案是由小的答案推出来的 51 { 52 while(query[i].w>=edge[cnt].w) 53 { 54 int u = findf(edge[cnt].u),v = findf(edge[cnt].v); 55 cnt++; 56 if(u==v) continue; 57 p[u] = v; 58 query[i].ans += sz[u]*sz[v]; 59 sz[v]+=sz[u]; 60 } 61 query[i].ans += query[i-1].ans; 62 } 63 sort(query+1,query+q+1,cmp()); 64 for(int i=1;i<=q;i++) 65 printf("%d\n",query[i].ans); 66 } 67 return 0; 68 }