2017年浙江省赛总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017年浙江省赛总结相关的知识,希望对你有一定的参考价值。
最终是5题银。其实感觉再给点时间能7题的,主要是最后机子不够用了,没时间调试了,当然代码能力弱也是很大的一个问题。
E题,队友当时卡了很久,最终是A了。赛后发现就是一个很水的数位DP。。代码如下:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 using namespace std; 5 typedef long long ll; 6 7 int T,n; 8 char s[15]; 9 int bit[15]; 10 ll dp[15][200]; 11 int val[] = {6,2,5,5,4,5,6,3,7,6,6,5,4,5,5,4}; 12 int get(char c) 13 { 14 if(c <= ‘9‘) return c - ‘0‘; 15 return c - ‘A‘ + 10; 16 } 17 ll dfs(int pos,int sum,bool flag) 18 { 19 if(pos == -1) return sum; 20 ll& ans = dp[pos][sum]; 21 if(flag && ans!=-1) return ans; 22 int d = flag?15:bit[pos]; 23 24 ll ret = 0; 25 for(int i=0;i<=d;i++) 26 { 27 ret += dfs(pos-1,sum+val[i],flag||i<d); 28 } 29 if(flag) ans = ret; 30 return ret; 31 } 32 ll solve(ll x) 33 { 34 if(x == -1) return 0; 35 // 要清空,因为下面固定从第7位开始 36 memset(bit,0,sizeof bit); 37 int pos = 0; 38 while(x) 39 { 40 bit[pos++] = x % 16; 41 x /= 16; 42 } 43 44 // 固定从第7位开始,因为前导0也算贡献 45 return dfs(7,0,false); 46 } 47 48 int main() 49 { 50 scanf("%d",&T); 51 memset(dp,-1,sizeof(dp)); 52 while(T--) 53 { 54 scanf("%d%s",&n,s); 55 ll st = 0, ed = 0; 56 for(int i=0;s[i];i++) st = st * 16 + get(s[i]); 57 ed = st + n - 1; 58 ll lim = 0; 59 for(int i=0;s[i];i++) lim = lim * 16 + get(‘F‘); 60 ll ans = 0; 61 if(ed <= lim) ans = solve(ed) - solve(st - 1); 62 else ans = solve(ed - lim - 1) + solve(lim) - solve(st - 1); 63 printf("%lld\n",ans); 64 } 65 return 0; 66 }
F题,现场用线段树写,就写错了一个字母然后RE了QAQ,,把cnt[l]改成cnt[o]就A了。要注意的是因为在query内带修改,因此再返回前需要up一下(虽然只在最后up,返回前没up也能够A)。线段树代码如下:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <set> 5 #include <vector> 6 #define t_mid (l+r>>1) 7 #define ls (o<<1) 8 #define rs (o<<1|1) 9 #define lson ls,l,t_mid 10 #define rson rs,t_mid+1,r 11 using namespace std; 12 const int N = 1e5 + 5; 13 14 int a[N]; 15 int fa[N]; 16 vector<int> v[N],ans[N]; 17 int cnt[N<<2]; 18 19 int n; 20 int get_fa(int u) 21 { 22 if(fa[u] == -1) return u; 23 else return fa[u] = get_fa(fa[u]); 24 } 25 void up(int o) {cnt[o] = cnt[ls] + cnt[rs];} 26 int query(int o,int l,int r,int pos) 27 { 28 if(l == r) 29 { 30 if(cnt[o] == 0) return -1; 31 else 32 { 33 cnt[o]--; 34 int t = v[l].back(); v[l].pop_back(); 35 return t; 36 } 37 } 38 if(pos > t_mid && cnt[rs] > 0) 39 { 40 int t = query(rson,pos); 41 up(o); 42 if(t != -1) return t; 43 else 44 { 45 if(cnt[ls] == 0) return -1; 46 else 47 { 48 int ret = query(lson,pos); 49 up(o); 50 return ret; 51 } 52 } 53 } 54 55 if(cnt[ls] == 0) return -1; 56 else 57 { 58 int ret = query(lson,pos); 59 up(o); 60 return ret; 61 } 62 //up(o); 63 } 64 void update(int o,int l,int r,int pos) 65 { 66 if(l == r) 67 { 68 cnt[o] += 2; 69 return ; 70 } 71 if(t_mid >= pos) update(lson,pos); 72 else update(rson,pos); 73 up(o); 74 } 75 void build(int o,int l,int r) 76 { 77 cnt[o] = 0; 78 if(l == r) return ; 79 build(lson); 80 build(rson); 81 } 82 83 int main() 84 { 85 int T; 86 scanf("%d",&T); 87 while(T--) 88 { 89 scanf("%d",&n); 90 for(int i=1;i<=n;i++) fa[i] = -1; 91 build(1,1,n); 92 for(int i=1;i<=n;i++) scanf("%d",a+i); 93 for(int i=1;i<=n;i++) 94 { 95 ans[i].clear(); 96 v[i].clear(); 97 } 98 for(int i=1;i<=n;i++) 99 { 100 int t = query(1,1,n,a[i]); 101 fa[i] = t; 102 update(1,1,n,a[i]); 103 v[a[i]].push_back(i); 104 v[a[i]].push_back(i); 105 } 106 for(int i=1;i<=n;i++) get_fa(i); 107 for(int i=1;i<=n;i++) 108 { 109 if(fa[i] != -1) ans[fa[i]].push_back(i); 110 else ans[i].push_back(i); 111 } 112 int ans_cnt = 0; 113 for(int i=1;i<=n;i++) if(ans[i].size()) ans_cnt++; 114 printf("%d\n",ans_cnt); 115 for(int i=1;i<=n;i++) 116 { 117 if(ans[i].size()) 118 { 119 printf("%d",ans[i].size()); 120 for(int j=0;j<ans[i].size();j++) printf(" %d",ans[i][j]); 121 puts(""); 122 } 123 } 124 } 125 return 0; 126 }
另外比赛的时候就想到了贪心的方法,每次选择不超过a[i]的最大的能插入的数字后面插入即可(不能插入就新建一棵树),当时因为以为set没有lower_bound(以为这是线性的),所以找不到一个容器能够在log的时间内查找,删除和插入,故没用这个方法写。事实上,set自带的成员函数lower_bound是log的,而如果lower_bound里面丢set的话复杂度要高很多(这似乎是一个STL经常遇到的问题。。)。如果知道了set也能够log的时间内查找的话,代码就不难写了。代码如下:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <set> 5 #include <vector> 6 using namespace std; 7 const int N = 1e5 + 5; 8 9 int a[N]; 10 int fa[N]; 11 vector<int> ans[N]; 12 13 struct node 14 { 15 int val, id, cnt; 16 bool operator < (const node & temp) const 17 { 18 if(val == temp.val) 19 { 20 if(id == temp.id) return cnt < temp.cnt; 21 else return id < temp.id; 22 } 23 else return val < temp.val; 24 } 25 }; 26 set<node> S; 27 int n; 28 int get_fa(int u) 29 { 30 if(fa[u] == -1) return u; 31 else return fa[u] = get_fa(fa[u]); 32 } 33 34 int main() 35 { 36 set<node>::iterator it, temp; 37 int T; 38 scanf("%d",&T); 39 while(T--) 40 { 41 int cnt = 1; 42 scanf("%d",&n); 43 S.clear(); 44 //for(int i=1;i<=n;i++) fa[i] = -1; 45 int maxn = 0; 46 for(int i=1;i<=n;i++) scanf("%d",a+i), maxn = max(maxn, a[i]); 47 for(int i=1;i<=n;i++) ans[i].clear(); 48 for(int i=1;i<=n;i++) 49 { 50 if(S.size() == 0) 51 { 52 S.insert((node){a[i], i, 0}); 53 fa[i] = -1; 54 continue; 55 } 56 it = S.lower_bound((node){a[i], -1, -1}); 57 if(it == S.end()) 58 { 59 node now = *S.rbegin(); 60 fa[i] = now.id; 61 now.cnt++; 62 S.erase(*S.rbegin()); 63 if(now.cnt < 2) S.insert(now); 64 } 65 else 66 { 67 if(it->val == a[i]) 68 { 69 node now = *it; 70 fa[i] = now.id; 71 now.cnt++; 72 S.erase(*it); 73 if(now.cnt < 2) S.insert(now); 74 } 75 else 76 { 77 if(it == S.begin()) 78 { 79 cnt ++; 80 fa[i] = -1; 81 } 82 else 83 { 84 it--; 85 node now = *it; 86 fa[i] = now.id; 87 now.cnt++; 88 S.erase(*it); 89 if(now.cnt < 2) S.insert(now); 90 } 91 } 92 } 93 94 S.insert((node){a[i], i, 0}); 95 } 96 for(int i=1;i<=n;i++) get_fa(i); 97 for(int i=1;i<=n;i++) 98 { 99 if(fa[i] == -1) ans[i].push_back(i); 100 else ans[fa[i]].push_back(i); 101 } 102 printf("%d\n",cnt); 103 for(int i=1;i<=n;i++) 104 { 105 if(ans[i].size()) 106 { 107 printf("%d",ans[i].size()); 108 for(int j=0;j<ans[i].size();j++) printf(" %d",ans[i][j]); 109 puts(""); 110 } 111 } 112 } 113 return 0; 114 }
另外要注意的是题目说所有的n加起来不超过2e6,但是没说T的大小,因此T可能很多(事实上就是如此,有很多n很小的数据),因此不能够用memset直接初始化所有的数据,否则会T。
H题,是个xjbg题,主要思想就是分治递归,代码细节可能有点多,就直接丢队友AC的代码了:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+100; 4 5 int a[N],b[N],aid[N],bid[N]; 6 int ans[N]; 7 int dfs(int al,int ar,int bl,int br,int f){ 8 if(al == ar) { 9 ans[a[al]] = a[f]; 10 return -1; 11 } 12 if(a[al] == b[bl]){ 13 ans[a[al]] = a[f]; 14 int tmp = dfs(al+1,ar,bl+1,br,al); 15 if(tmp == -1) return -1; 16 if(a[al+1] == b[bl+1]) 17 return dfs(tmp,ar,bid[a[tmp]],br,al); 18 else return tmp; 19 } 20 else{ 21 dfs(al,aid[b[bl]]-1,bid[a[al]],bid[a[al]]+aid[b[bl]]-al-1,f); 22 dfs(aid[b[bl]],aid[b[bl]]+bid[a[al]]-bl-1,bl,bid[a[al]]-1,f); 23 if(aid[b[bl]]+bid[a[al]]-bl > ar) return -1; 24 return aid[b[bl]]+bid[a[al]]-bl; 25 } 26 } 27 28 int main(){ 29 int T; 30 cin >> T; 31 while(T--){ 32 int n; 33 scanf("%d",&n); 34 for(int i= 1;i <= n;i ++){ 35 int now; 36 scanf("%d",&now); 37 a[i] = now; 38 aid[now] = i; 39 } 40 for(int i= 1;i <= n;i ++){ 41 int now; 42 scanf("%d",&now); 43 b[i] = now; 44 bid[now] = i; 45 } 46 dfs(1,n,1,n,0); 47 for(int i= 1;i <= n; i++){ 48 printf("%d%c",ans[i],i==n?‘\n‘:‘ ‘); 49 } 50 } 51 return 0; 52 }
G题,是个博弈论题,感觉是个莽题,只要胆子大敢交就能A。根据nim游戏的定义,先手者为了能赢肯定是要模仿后手的行动,但是此时先手者存在奇偶限制,故不能够完全模仿后手(比如先手只能拿奇时后手可以拿偶来逃出必败态)。所以先手为了能赢必须要在第一手去掉所有的限制,那么就转化成了Bob先手的nim游戏了。同时需要一些特判,比如如果限制堆过多(大于1),先手是不能够一次拿完的,因此必败;如果存在一堆,先手只能拿偶数,但是是奇数堆,很显然Alice是无论如何都不可能拿掉这堆的最后一个的,因此必败。另外需要注意,如果某堆个数是1且限制是只能拿奇数个,那么这堆和没有限制一样。代码如下:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 using namespace std; 5 const int N = 1e5 + 5; 6 7 int a[N],b[N]; 8 int T,n; 9 bool solve() 10 { 11 int cnt1 = 0, cnt2 = 0; 12 for(int i=1;i<=n;i++) 13 { 14 if(b[i] == 2 && a[i] % 2) return false; 15 if(b[i] == 1 && a[i] > 1) cnt1++; 16 else if(b[i] == 2) cnt2++; 17 } 18 if(cnt1 + cnt2 >= 2) return false; 19 int ans = 0; 20 for(int i=1;i<=n;i++) 21 { 22 if(b[i] == 1 && a[i] > 1) ans ^= (a[i] % 2 ? 0 : 1); 23 else if(b[i] == 2) ans ^= 0; 24 else ans ^= a[i]; 25 } 26 if(cnt1 + cnt2 == 1) return ans ? 0 : 1; 27 else return ans ? 1 : 0; 28 } 29 30 int main() 31 { 32 scanf("%d",&T); 33 while(T--) 34 { 35 scanf("%d",&n); 36 for(int i=1;i<=n;i++) scanf("%d",a+i); 37 for(int i=1;i<=n;i++) scanf("%d",b+i); 38 puts(solve() ? "Alice" : "Bob"); 39 } 40 return 0; 41 }
最后,今年省赛晚饭没有饮料和水果供应,差评!不过在食堂遇到了多年没见的老同学也还是挺开心的~
以上是关于2017年浙江省赛总结的主要内容,如果未能解决你的问题,请参考以下文章
2017年浙江中医药大学大学生程序设计竞赛(重现赛)D - CC的神奇背包
2017年浙江中医药大学大学生程序设计竞赛(重现赛)A - 不存在的树