2016 Multi-University Training Contest 1
A.Abandoned country
构建最小生成树,然后每条边的权值乘以两边的点数之积。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; const int maxn = 1e5+5; int n,m; struct Edge{ int u,v,w; }; Edge edge[10*maxn]; vector<Edge> g[maxn]; int in[maxn]; int pre[maxn]; int vis[maxn]; typedef long long ll; bool cmp(Edge A,Edge B) { return A.w<B.w; } ll sum = 0; double sum2 = 0; int Find(int x){return pre[x] == x ? x : pre[x] = Find(pre[x]);} int Merge(int x,int y) { int fx = Find(x); int fy = Find(y); if(fx != fy) { pre[fx] = fy; return 1; } return 0; } void Kruskal() { for(int i = 1; i<=m; i++) { if(Merge(edge[i].u,edge[i].v)) { sum += edge[i].w; g[edge[i].u].push_back((Edge){edge[i].u,edge[i].v,edge[i].w}); g[edge[i].v].push_back((Edge){edge[i].v,edge[i].u,edge[i].w}); in[edge[i].u]++; in[edge[i].v]++; } } } void inin() { for(int i = 1; i<=n; i++) { pre[i] = i; in[i] = 0; g[i].clear(); vis[i] = 0; } } int dfs(int x) { vis[x] = 1; int num = 1; for(int i = 0; i<g[x].size(); i++) { Edge cur = g[x][i]; if(vis[cur.v]) continue; if(in[cur.v]==1) { sum2 += (double)cur.w*(n-1); num += 1; continue; } int ver = dfs(cur.v); sum2 += (double)cur.w*ver*(n-ver); num += ver; } return num; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d %d",&n,&m); inin(); for(int i = 1; i<=m; i++) { scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w); } sort(edge+1,edge+m+1,cmp); sum = 0; Kruskal(); sum2 = 0; int i = 0; dfs(1); printf("%I64d %.2lf\n",sum,sum2*2/n/(n-1)); } return 0; } /* 421 7 6 1 2 1 2 3 2 3 4 3 3 5 4 3 6 5 3 7 6 */
D.GCD
RMQ-ST预处理出gcd,复杂度n*logn*gcd.不过查询复杂度为log*gcd.
第二问枚举起点,对每个起点,gcd分段递减,对每一段二分出段的尾部,然后这段的gcd值都相同,用map记录,每个起点最多logn段。复杂度n*logn*logn*gcd。查询logn
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <map> using namespace std; const int maxn = 1e5+5; int n; int cnt[maxn]; int M[maxn][20]; map<int,long long> g; int gcd(int a,int b) { return b == 0 ? a : gcd(b,a%b); } void inin() { for(int i = 1; i<=n; i++) M[i][0] = cnt[i]; for(int j = 1; (1 << j) <= n; j++) for(int i = 1; (i + (1 << j)-1) <= n; i++) M[i][j] = gcd( M[i][j-1] , M[i + (1 << (j-1))][j-1]); } int query(int l,int r) { int k = 0; while((1 << (k+1))<=(r-l+1)) k++; //r-2^k+1>=l+2^k return gcd(M[l][k],M[r-(1 << k)+1][k]); } int binary(int s,int head,int g) { int L = head; int R = n; int result = head; while(L<=R) { int mid = (L+R)/2; if(query(s,mid)==g) { result = mid; L = mid+1; } else R = mid-1; } return result; } void pre() { g.clear(); for(int i = 1; i<=n; i++) { int head = i; while(head<=n) { int Gcd = query(i,head); int tail = binary(i,head,Gcd); g[Gcd] += tail-head+1; head = tail+1; } } } int main() { int t,kase = 0; cin>>t; while(t--) { printf("Case #%d:\n",++kase); scanf("%d",&n); for(int i = 1; i<=n; i++) { scanf("%d",&cnt[i]); } inin(); pre(); int q,l,r; cin>>q; for(int i = 1; i<=q; i++) { scanf("%d %d",&l,&r); int gg = query(l,r); printf("%d %I64d\n",gg,g[gg]); } } return 0; }