官方题解:http://bestcoder.hdu.edu.cn/blog/2016-multi-university-training-contest-1-solutions-by-hit/
题目链接:
A http://acm.hdu.edu.cn/showproblem.php?pid=5723
第一步求最小生成树,因为题目限制边权各不相同,所以最小生成树唯一。第二步求任意树上任意两点距离的均值,通过计算每条边对总和的贡献得到。枚举每条边,经过次数等于该边左边点的个数乘右边点的个数。dfs求有根树每个点子树节点个数的办法。

1 #include<bits/stdc++.h>
2 using namespace std;
3 const int M=1e6+10;
4 struct E {
5 int u,v;
6 double w;
7 } e[M];
8 int n,m;
9 double cost;
10 double average;
11 struct G{
12 struct E{
13 int v,next;
14 double w;
15 }e[M];
16 int le,head[M];
17 void init(int n){
18 le=0;
19 for(int i=0;i<=n;i++) head[i]=-1;
20 }
21 void add(int u,int v,double w){
22 e[le].v=v;
23 e[le].w=w;
24 e[le].next=head[u];
25 head[u]=le++;
26 }
27 }g;
28 class Kruskal { ///最小生成树(无向图) O(E*log(E))
29 typedef double typec; ///边权的类型
30 static const int ME=1e6+10; ///边的个数
31 static const int MV=1e5+10; ///点的个数
32 class UnionFindSet { ///并查集
33 int par[MV];
34 void add(int son,int fa) {
35 par[fa]+=par[son];
36 par[son]=fa;
37 }
38 public:
39 void init(int n) {
40 for(int i=0; i<=n; i++) par[i]=-1;
41 }
42 int getroot(int x) {
43 int i=x,j=x,temp;
44 while(par[i]>=0) i=par[i];
45 while(j!=i) {
46 temp=par[j];
47 par[j]=i;
48 j=temp;
49 }
50 return i;
51 }
52 bool unite(int x,int y) {
53 int p=getroot(x);
54 int q=getroot(y);
55 if(p==q) return false;
56 if(par[p]>par[q]) {
57 add(p,q);
58 }
59 else {
60 add(q,p);
61 }
62 return true;
63 }
64 } ufs;
65 struct E {
66 int u,v;
67 typec w;
68 friend bool operator < (const E &a,const E &b) {
69 return a.w<b.w;
70 }
71 } e[ME];
72 int le,num,n;
73 typec res;
74 public:
75 void init(int tn) { ///传入点的个数
76 n=tn;
77 le=0;
78 }
79 void add(int u,int v,typec w) {
80 e[le].u=u;
81 e[le].v=v;
82 e[le].w=w;
83 le++;
84 }
85 typec solve() { ///返回-1 不连通
86 res=0;
87 num=1;
88 ufs.init(n);
89 sort(e,e+le);
90 for(int i=0; i<le&&num<n; i++) {
91 if(ufs.unite(e[i].u,e[i].v)) {
92 num++;
93 res+=e[i].w;
94 g.add(e[i].u,e[i].v,e[i].w);
95 g.add(e[i].v,e[i].u,e[i].w);
96 }
97 }
98 if(num<n) res=-1;
99 return res;
100 }
101 } mst;
102 int dfs(int u,int fa){
103 int sum=1;
104 for(int i=g.head[u];~i;i=g.e[i].next){
105 int v=g.e[i].v;
106 if(v==fa) continue;
107 double w=g.e[i].w;
108 int son=dfs(v,u);
109 sum+=son;
110 average+=w*son*(n-son);
111 }
112 return sum;
113 }
114 void solve() {
115 g.init(n);
116 mst.init(n);
117 for(int i=0;i<m;i++){
118 mst.add(e[i].u,e[i].v,e[i].w);
119 }
120 cost=mst.solve();
121 average=0;
122 dfs(1,-1);
123 average*=2;
124 average/=n;
125 average/=(n-1);
126 }
127 int main() {
128 int t;
129 while(~scanf("%d",&t)) {
130 while(t--) {
131 scanf("%d%d",&n,&m);
132 for(int i=0; i<m; i++) {
133 scanf("%d%d%lf",&e[i].u,&e[i].v,&e[i].w);
134 }
135 solve();
136 printf("%.0f %.2f\n",cost,average);
137 }
138 }
139 return 0;
140 }
B http://acm.hdu.edu.cn/showproblem.php?pid=5724
博弈,nim,
标程是递推的,会比我快一点,我写的是记忆化搜素,更符合正向思维。sg函数=所有后继状态sg值中,最小的不出现的非负整数。定理。多个游戏组合的和,等于多个游戏sg值的异或。

1 #include<bits/stdc++.h>
2 #define mt(a,b) memset(a,b,sizeof(a))
3 using namespace std;
4 const int M=1e3+10;
5 int n,m[M];
6 int a[M][M];
7 char answer[2][8]={"NO","YES"};
8 int sg[1<<20];
9 void init(){
10 mt(sg,-1);
11 }
12 int dfs(int sta){
13 int &g=sg[sta];
14 if(~g) return g;
15 bool s[20];
16 mt(s,0);
17 int zero=-1;
18 for(int i=0;i<20;i++){
19 if(!((sta>>i)&1)){
20 zero=i;
21 continue;
22 }
23 if(zero==-1) continue;
24 s[dfs(sta^(1<<i)^(1<<zero))]=true;
25 }
26 for(int i=0;i<32;i++){
27 if(s[i]) continue;
28 g=i;
29 break;
30 }
31 return g;
32 }
33 int solve(){
34 int nim=0;
35 for(int i=0;i<n;i++){
36 int sta=0;
37 for(int j=0;j<m[i];j++){
38 sta|=(1<<(20-a[i][j]));
39 }
40 nim^=dfs(sta);
41 }
42 return nim!=0;
43 }
44 int main(){
45 init();
46 int t;
47 while(~scanf("%d",&t)){
48 while(t--){
49 scanf("%d",&n);
50 for(int i=0;i<n;i++){
51 scanf("%d",&m[i]);
52 for(int j=0;j<m[i];j++){
53 scanf("%d",&a[i][j]);
54 }
55 }
56 puts(answer[solve()]);
57 }
58 }
59 return 0;
60 }
递推预处理写法

1 #include<bits/stdc++.h>
2 #define mt(a,b) memset(a,b,sizeof(a))
3 using namespace std;
4 const int M=1e3+10;
5 const int total=1<<20;
6 int n,m[M];
7 int a[M][M];
8 char answer[2][8]={"NO","YES"};
9 int sg[total];
10 bool had[20];
11 void init(){
12 for(int i=0;i<total;i++){
13 mt(had,0);
14 int zero=-1;
15 for(int j=0;j<20;j++){
16 if(!((i>>j)&1)){
17 zero=j;
18 continue;
19 }
20 if(zero==-1) continue;
21 had[sg[i^(1<<j)^(1<<zero)]]=true;
22 }
23 for(int j=0;;j++){
24 if(had[j]) continue;
25 sg[i]=j;
26 break;
27 }
28 }
29 }
30 int solve(){
31 int nim=0;
32 for(int i=0;i<n;i++){
33 int sta=0;
34 for(int j=0;j<m[i];j++){
35 sta|=(1<<(20-a[i][j]));
36 }
37 nim^=sg[sta];
38 }
39 return nim!=0;
40 }
41 int main(){
42 init();
43 int t;
44 while(~scanf("%d",&t)){
45 while(t--){
46 scanf("%d",&n);
47 for(int i=0;i<n;i++){
48 scanf("%d",&m[i]);
49 for(int j=0;j<m[i];j++){
50 scanf("%d",&a[i][j]);
51 }
52 }
53 puts(answer[solve()]);
54 }
55 }
56 return 0;
57 }
D http://acm.hdu.edu.cn/showproblem.php?pid=5726
第一问求一个区间的gcd,第二问求等于这个值的有多少个区间。比赛时的做法,第一问通过rmq预处理,复杂度n*logn*gcd,查询gcd。 第二问枚举起点,对每个起点,gcd分段递减,对每一段二分出段的尾部,然后这段的gcd值都相同,用map记录,每个起点最多logai段。复杂度n*logn*loga*gcd。查询log。

1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long LL;
4 const int M=1e5+10;
5 int n,m;
6 int a[M];
7 struct Q {
8 int x,y,gcd;
9 LL sum;
10 } q[M];
11 int gcd(int a,int b){
12 return b?gcd(b,a%b):a;
13 }
14 class RMQ { ///区间最值查询(ST)离线算法 init O(n*logn) query O(1)
15 typedef int typec; ///点权类型
16 static const int M=1e5+10; ///点的个数
17 int LOG[M];
18 typec dpmax[M][20];
19 public:
20 RMQ() {
21 LOG[0]=-1;
22 for(int i=1; i<M; i++) {
23 LOG[i]=LOG[i>>1]+1;
24 }
25 }
26 void init(int n,typec a[]) { ///传入点的个数,下标 1 开始
27 for(int i=1; i<=n; i++) {
28 dpmax[i][0]=a[i];
29 }
30 for(int j=1; j<=LOG[n]; j++) {
31 for(int i=1; i+(1<<j)-1<=n; i++) {
32 int k=i+(1<<(j-1));
33 dpmax[i][j]=gcd(dpmax[i][j-1],dpmax[k][j-1]);
34 }
35 }
36 }
37 typec get(int a,int b) { ///传入 1 返回 max,传入 0 返回 min
38 int k=LOG[b-a+1];
39 b=b-(1<<k)+1;
40 return gcd(dpmax[a][k],dpmax[b][k]);
41 }
42 } rmq;
43 map<int,LL> mp;
44 int binary(int s,int head,int g){
45 int L=head,R=n,result=L;
46 while(L<=R){
47 int mid=(L+R)>>1;
48 if(rmq.get(s,mid)==g){
49 result=mid;
50 L=mid+1;
51 }
52 else{
53 R=mid-1;
54 }
55 }
56 return result;
57 }
58 void init() {
59 mp.clear();
60 for(int i=1;i<=n;i++){
61 int head=i;
62 while(head<=n){
63 int g=rmq.get(i,head);
64 int tail=binary(i,head,g);
65 mp[g]+=tail-head+1;
66 head=tail+1;
67 }
68 }
69 }
70 void solve() {
71 rmq.init(n,a);
72 init();
73 for(int i=0;i<m;i++){
74 q[i].gcd=rmq.get(q[i].x,q[i].y);
75 q[i].sum=mp[q[i].gcd];
76 }
77 }
78 int main() {
79 int t;
80 while(~scanf("%d",&t)) {
81 int cas=1;
82 while(t--) {
83 scanf("%d",&n);
84 for(int i=1; i<=n; i++) {
85 scanf("%d",&a[i]);
86 }
87 scanf("%d",&m);
88 for(int i=0; i<m; i++) {
89 scanf("%d%d",&q[i].x,&q[i].y);
90 }
91 solve();
92 printf("Case #%d:\n",cas++);
93 for(int i=0; i<m; i++) {
94 printf("%d %I64d\n",q[i].gcd,q[i].sum);
95 }
96 }
97 }
98 return 0;
99 }
D 标程的解法会少一个logn,因为找不同的gcd段时不二分,而是把每一个点作为终点的情况存下,存某个终点各个段的gcd值和起点。当处理 x 为终点的时, x-1的段的结果可以利用上,类似单调队列。也就是说,增加了a【x】,前面的段断点都可能是新的断点,实现起来不太好理解。

1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long LL;
4 const int M=1e5+10;
5 int n,m;
6 int a[M];
7 struct Q {
8 int x,y,gcd;
9 LL sum;
10 } q[M];
11 int gcd(int a,int b){
12 return b?gcd(b,a%b):a;
13 }
14 map<int,LL> mp;
15 typedef pair<int,int> pii;
16 vector<pii> block[M];
17 void solve() {
18 mp.clear();
19 for(int i=0;i<=n;i++){
20 block[i].clear();
21 }
22 for(int i=1;i<=n;i++){
23 int last=0;
24 for(int j=0;j<block[i-1].size();j++){
25 int head=block[i-1][j].first;
26 int g=block[i-1][j].second;
27 int nowgcd=gcd(g,a[i]);
28 if(nowgcd==last) continue;
29 last=nowgcd;
30 block[i].push_back(make_pair(head,nowgcd));
31 }
32 if(a[i]!=last){
33 block[i].push_back(make_pair(i,a[i]));
34 }
35 for(int j=0;j<block[i].size();j++){
36 int head=block[i][j].first;
37 int g=block[i][j].second;
38 int tail=i;
39 if(j+1<block[i].size()){
40 tail=block[i][j+1].first-1;
41 }
42 mp[g]+=tail-head+1;
43 }
44 }
45 for(int i=0;i<m;i++){
46 int L=0,R=block[q[i].y].size()-1,result=L;
47 while(L<=R){
48 int mid=(L+R)>>1;
49 if(block[q[i].y][mid].first<=q[i].x){
50 result=mid;
51 L=mid+1;
52 }
53 else{
54 R=mid-1;
55 }
56 }
57 q[i].gcd=block[q[i].y][result].second;
58 q[i].sum=mp[q[i].gcd];
59 }
60 }
61 int main() {
62 int t;
63 while(~scanf("%d",&t)) {
64 int cas=1;
65 while(t--) {
66 scanf("%d",&n);
67 for(int i=1; i<=n; i++) {
68 scanf("%d",&a[i]);
69 }
70 scanf("%d",&m);
71 for(int i=0; i<m; i++) {
72 scanf("%d%d",&q[i].x,&q[i].y);
73 }
74 solve();
75 printf("Case #%d:\n",cas++);
76 for(int i=0; i<m; i++) {
77 printf("%d %I64d\n",q[i].gcd,q[i].sum);
78 }
79 }
80 }
81 return 0;
82 }
end
来源:https://www.cnblogs.com/gaolzzxin/p/5688015.html
