2016多校赛1 A 期望 B SG博弈,状压 D 倍增,二分
2016 Multi-University Training Contest 1
题意:给定一个图,1、求最小生成树的边权和。2、求最小生成树上任意两个点的距离的期望。
tags:第一问好求。第二问,ans=(所有点对距离和)/C(n,2), 这里转一下思维,所有点对距离和 --> 每条边要用到几次, 然后dfs搜一遍即可。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define fi first #define se second typedef long long ll; const int N = 100005, M=1000005; int n, m, sz[N], fa[N], head[N], tot1, tot2; ll ans1, ans2; pair<ll, pair<int ,int > > e1[M]; struct Edge{ int to, next; ll w; } e[M]; void Init() { tot1=tot2=0; ans1=ans2=0; rep(i,1,n) fa[i]=i; mes(head, -1); } void Addedge(int u, int v, ll w) { e[tot2]={v, head[u], w}; head[u]=tot2++; e[tot2]={u, head[v], w}; head[v]=tot2++; } void dfs(int u, int fa) { sz[u]=1; for(int i=head[u]; i!=-1; i=e[i].next) { int to=e[i].to; if(to!=fa) { dfs(to, u); sz[u]+=sz[to]; ans2+= e[i].w*(n-sz[to])*sz[to];//*(e[i].w); //一开始这里爆int 挖了 } } } int Find(int x) { return fa[x]==x ? x : fa[x]=Find(fa[x]); } bool Unite(int u, int v) { int fau=Find(u), fav=Find(v); if(fau==fav) return false; else { fa[fav]=fau; return true; } } void kruskal() { sort(e1+1, e1+1+tot1); int num=0; rep(i,1,tot1) if(Unite(e1[i].se.fi, e1[i].se.se)) { num++, ans1+=e1[i].fi; Addedge(e1[i].se.fi, e1[i].se.se, e1[i].fi); if(num==n-1) break; } printf("%lld ", ans1); } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d %d", &n, &m); Init(); int u, v; ll w; rep(i,1,m) { scanf("%d %d %lld", &u, &v, &w); e1[++tot1]=MP(w, MP(u,v)); } kruskal(); dfs(1,0); printf("%.2f\n", 1.0*ans2*2/n/(n-1)); } return 0; }
题意:一个n*20的棋盘,给出每行棋子的位置。如果棋子右边一格没有棋子,它可以移动到右边一格;如果右边一格有棋子,它可以跳过右边连着的所有棋子。 两个人轮流选一个棋子移动,不能移动者败,问先手胜负。
tags:20格,状压存下状态,推出sg即可。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f typedef long long ll; const int N = 1<<21; int n, m, ans, pi, sg[N]; int getsg(int x) { bool vis[21]; mes(vis, false); rep(i,1,20) if((x>>(i-1)&1)) { per(j,i-1,1) if(!((x>>(j-1))&1)) { int tmp=x^(1<<(i-1))^(1<<(j-1)); vis[sg[tmp]]=1; break; } } rep(i,0,20) if(vis[i]==0) return i; } int main() { rep(i,0,N-1) sg[i]=getsg(i); int T; scanf("%d", &T); while(T--) { scanf("%d", &n); ans=0; rep(ca,1,n) { scanf("%d", &m); int tmp=0; rep(i,1,m) { scanf("%d", &pi); tmp|= 1<<(20-pi); } ans^= sg[tmp]; } if(ans==0) puts("NO"); else puts("YES"); } return 0; }
题意:给出数组a[],Q个询问,每次询问有 l , r 。求 1、区间[l, r]的gcd; 2、求有多少个区间的gcd和其相等。
tags: 好题
第一问好求,倍增预处理出即可。 重点是第二问,如何统计出gcd出现的次数。我们将左端点从1到n枚举,同时右端点从左端点开始往右边走。因为左端点固定,右端点往右走,gcd肯定是下降的,而且下降速度会很快,所以我们同时再二分处理即可,复杂度O(n*log(n)*log(n))。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a;i<=b;i++) #define per(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f typedef long long ll; const int N = 100005; int n, Q, a[N], b[N][3], g[N][20]; map<int , ll > mp; int query(int l, int r) { int flr=log2(r-l+1), len=1<<flr; return __gcd(g[l][flr], g[r-len+1][flr]); } void Init() { int tlog=log2(n); rep(j,1,n) g[j][0]=a[j]; rep(i,1,tlog) { int len=1<<(i-1), nr=n-(1<<i)+1; rep(j,1,nr) g[j][i]=__gcd(g[j][i-1], g[j+len][i-1]); } } int main() { int T; scanf("%d", &T); rep(cas,1,T) { mp.clear(); printf("Case #%d:\n", cas); scanf("%d", &n); rep(i,1,n) scanf("%d", &a[i]); Init(); scanf("%d", &Q); rep(i,1,Q) { scanf("%d %d", &b[i][0], &b[i][1]); b[i][2]=query(b[i][0], b[i][1]); mp.insert(make_pair(b[i][2], 0)); } rep(i,1,n) { int ti=i; while(ti<=n) { int tmp=query(i, ti); int l=ti, r=n, ans1; while(l<=r) { int mid=(l+r)>>1; if(query(i,mid)>=tmp) l=mid+1, ans1=mid; else r=mid-1; } if(mp.find(tmp)!=mp.end()) mp[tmp]+= ans1-ti+1; ti=ans1+1; } } rep(i,1,Q) { printf("%d %lld\n", b[i][2], mp[b[i][2]]); } } return 0; }
tags:计算几何,求四面体内接球的球心坐标和半径。 传送门