2016 Multi-University Training Contest 1

官方题解: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 }
View Code

 

 

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 }
View Code

 

递推预处理写法

 

 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 }
View Code

 

 

 

 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 }
View Code

 

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 }
View Code

 

 

end

posted on 2016-07-20 13:32  gaolzzxin  阅读(383)  评论(0编辑  收藏  举报