第一场多校,出了一题,,没有挂零还算欣慰。
1001,求最小生成树和,确定了最小生成树后任意两点间的距离的最小数学期望。当时就有点矛盾,为什么是求最小的数学期望以及为什么题目给了每条边都不相等的条件。看了题解以后才明白:“首先注意到任意两条边的边权是不一样的,由此得知最小生成树是唯一的,最小生成树既然 是唯一的,那么期望其实也就是唯一的,不存在什么最小期望。”那么第一问最小生成树只要克鲁斯卡尔算法即可,第二问,总的路的条数是n*(n-1)/2,然后确定所有路的权值和的方法是:枚举每一条边,这一条边出现的次数是,它左右的点数之积。
1002,博弈论,用状态压缩预处理出一行的状态,然后各行的sg值异或一下即可。代码如下:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <math.h> 5 #include <map> 6 using namespace std; 7 typedef long long ll; 8 const int N = 20 + 5; 9 10 int sg[1<<20],s[N]; 11 12 void init() 13 { 14 for(int i=1;i<(1<<20);i++) 15 { 16 int last = -1; 17 memset(s,-1,sizeof(s)); 18 // 从右边为正 19 for(int j=0;j<20;j++) 20 { 21 if(!((i>>j) & 1)) // 找到第一个是0的位置(空位) 22 { 23 last = j; 24 } 25 if((i>>j) & 1) // 再找到过去第一个不是空位的地方 26 { 27 if(last != -1) 28 { 29 s[sg[i^(1<<last)^(1<<j)]] = 1; 30 } 31 } 32 } 33 34 int pos = 0; 35 while(s[pos] != -1) pos++; 36 sg[i] = pos; 37 } 38 } 39 40 int main() 41 { 42 init(); 43 int T; 44 scanf("%d",&T); 45 while(T--) 46 { 47 int ans = 0; 48 int n; 49 scanf("%d",&n); 50 for(int i=1;i<=n;i++) 51 { 52 int temp = 0; 53 int m; 54 scanf("%d",&m); 55 for(int j=1;j<=m;j++) 56 { 57 int t; 58 scanf("%d",&t); 59 temp ^= 1<<(20-t); // 假设是正数过去的19,那么是1<<1,即右数过来的第二位 60 } 61 ans ^= sg[temp]; 62 } 63 puts(ans?"YES":"NO"); 64 } 65 }
1004,线段树,询问指定区间内的gcd以及整个1~n区间内,gcd等于这个gcd的区间的个数。即使看了题解也很懵逼,网上找了一份貌似是rmq的代码,很巧妙,具体见代码和注释吧:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <math.h> 5 #include <map> 6 using namespace std; 7 typedef long long ll; 8 const int N = 100000 + 5; 9 10 int T,n,m,a[N]; 11 int mp[N][65]; 12 map<int,ll> M; 13 14 int gcd(int a,int b) {return a%b?gcd(b,a%b):b;} 15 16 void init() 17 { 18 M.clear(); 19 int m = log(n)/log(2); 20 // mp的含义是从第i个数开始,长度为(1<<j)(包含自身),这个区间内数的gcd 21 // 以i开头的区间每次多加一个数字如果让gcd发生变小,gcd的值都至少除以2(最小因子为2) 22 for(int i=1;i<=n;i++) mp[i][0] = a[i]; 23 for(int i=1;i<=m;i++) 24 { 25 // 因为mp的值是根据后面的值来更新的,所以j从大到小 26 for(int j=n;j>=1;j--) 27 { 28 if(j+(1<<(i-1)) <= n) mp[j][i] = gcd(mp[j][i-1],mp[j+(1<<(i-1))][i-1]); 29 else mp[j][i] = mp[j][i-1]; 30 } 31 } 32 } 33 34 // 用于查询l~r区间内的gcd 35 int query(int l,int r) 36 { 37 int m = log(r-l+1)/log(2); 38 return gcd(mp[l][m],mp[r-(1<<m)+1][m]); 39 } 40 41 int main() 42 { 43 scanf("%d",&T); 44 for(int kase=1;kase<=T;kase++) 45 { 46 scanf("%d",&n); 47 for(int i=1;i<=n;i++) scanf("%d",a+i); 48 init(); 49 50 // 因为temp的变化是log级别的,所以总的复杂度是n(log(n))^2 51 for(int i=1;i<=n;i++) 52 { 53 int temp = a[i]; // temp表示从i开始的这个区间,不断变化着的那个gcd的值 54 int l = i, r = n, mid, o = l; 55 while(temp > 1) // temp = 1的话后面的gcd就一直是1了 56 { 57 r = n, o = l; // o是指新区间的起始点 58 int ans = -1; 59 // 二分是为了找到gcd为temp的最长区间 60 while(l <= r) 61 { 62 mid = l + r >> 1; 63 if(query(i,mid) < temp) r = mid - 1; 64 else 65 { 66 l = mid + 1; 67 ans = mid; // 这里为什么一定要用ans记录而不能直接使用mid 68 // 可以那样例的1 2 4 6 7举例子 69 // i=2时,l和r都是5时,ans还是4,mid是5 70 // 那么,temp=2区间应该是2-4而不是2-5的 71 // 原因在于如果i~mid的区间的gcd比temp小的话,ans是不能更新的,而mid仍会更新 72 } 73 } 74 M[temp] += ans - o + 1; 75 76 l = ans + 1; // l移动到下一个位置,来找下一个temp的区间 77 if(l > n) break; 78 temp = query(i,l); 79 } 80 // 要加这个条件是因为可能是 l > n 才break出来的 81 if(temp == 1) M[1] += n - l + 1; 82 } 83 84 printf("Case #%d: ",kase); 85 scanf("%d",&m); 86 while(m--) 87 { 88 int l,r; 89 scanf("%d%d",&l,&r); 90 int x = query(l,r); 91 printf("%d %I64d ",x,M[x]); 92 } 93 } 94 }
1011,纯几何题,直接丢个觉得写得不错的博客:http://blog.csdn.net/libin66/article/details/51957808。
另外,丢个觉得不错的资料:点我。其实空间四面体的内切球圆心求法和平面三角形内切圆圆心的求法很像,可以通过类比推出来,把边换成平面就行。