The Preliminary Contest for ICPC Asia Xuzhou 2019
A:Who is better?
题目链接:https://nanti.jisuanke.com/t/41383
题意:
类似于有N个石子,先手第一次不能拿完,每次后手只能拿 1 到 前一次拿的数量*2之间的数量,不能拿时则输
分析:
最近一直在刷博弈论的题,比赛的前一天晚上打的华东师范校赛第一题也刚好是道博弈题,说起来两者还是有点类似的地方
但是这题显然要难的多,好在我打完华东师范校赛的我又狠狠的把博弈论专题看了一遍,也很巧的看到了这两篇博客
https://blog.csdn.net/dgq8211/article/details/7602807
https://blog.csdn.net/iteye_6233/article/details/82396581
所以推导的时候就留意了斐波那契博弈...
有涉及到取模不用说exgcd往里丢就完事,找到前几个答案后再把规律扔到http://oeis.org/
很快发现当N为斐波那契数的时候先手是必输的
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #include<map> 6 #define mm(a,n) memset(a, n, sizeof(a)) 7 using namespace std; 8 #define ll long long 9 template <class T> 10 void read(T &x) 11 { 12 x = 0; 13 char ch = getchar(); 14 while (!isdigit(ch)) 15 ch = getchar(); 16 while (isdigit(ch)) 17 x = x * 10 + ch - '0', ch = getchar(); 18 } 19 const int N = 1e3 + 10; 20 ll a[N], m[N]; 21 ll exgcd(ll a, ll b, ll &x, ll &y) 22 { 23 if (!b) 24 { 25 x = 1, y = 0; 26 return a; 27 } 28 ll d = exgcd(b, a % b, x, y); 29 ll z = x; 30 x = y; 31 y = z - y * (a / b); 32 return d; 33 } 34 ll winner(int n, ll *ans, ll *m) 35 { 36 ll a, b, c, d, x, y, t; 37 for (int i = 1; i < n; i++) 38 { 39 a = m[i - 1], b = m[i]; 40 c = ans[i] - ans[i - 1]; 41 d = exgcd(a, b, x, y); 42 if (c % d) 43 return -1; 44 t = b / d; 45 x = (x * (c / d) % t + t) % t; 46 ans[i] = ans[i - 1] + m[i - 1] * x; 47 m[i] = m[i] / d * m[i - 1]; 48 } 49 return ans[n - 1]; 50 } 51 map<ll,ll>haha; 52 int main() 53 { 54 ios::sync_with_stdio(false); 55 haha.clear(); 56 haha[1] = haha[2] = 1; 57 for(int i = 3; i < 88; ++i) haha[i] = haha[i-1]+haha[i-2]; 58 int n; 59 read(n); 60 for (int i = 0; i < n; i++) 61 { 62 read(m[i]); 63 read(a[i]); 64 } 65 ll ans = winner(n, a, m); 66 67 if (ans == -1) 68 cout << "Tankernb!" << endl; 69 else 70 { 71 72 int flag = 0; 73 for(int i=1; i<88; i++) 74 { 75 if(haha[i] == ans) 76 flag = 1; 77 } 78 if(flag) 79 cout << "Lbnb!" << endl; 80 else 81 cout << "Zgxnb!" << endl; 82 } 83 return 0; 84 }
B:so easy
题目链接:https://nanti.jisuanke.com/t/41384
题意:
对于每次操作如果a是1则删除b,如果a是2则输出第一个大于等于b的数
分析:
我的水平真是对不起B题的题名
本来的想法是开个map——对于没有删除的数它对应的key值就为初始值0,对于删除的数它对应的初始值为1
然后对于key值为0的数直接输出即可,对于key值为1的数用二分找到第一个大于等于它的、且key值为0的数
然而map只是用来保存状态因此无法直接调用stl自带的二分,and手写的二分又各种bug,所以就很无奈的卡了一会
后来思索了一会想到题目的询问次数q<=1e6, 那么涉及到的数最多也只有1e6个,所以直接将所有读入的数保存+离散搞了一波
却很尴尬的WA了(这时候内心已经崩溃了)。 紧接着开始观察代码,因为比较长看得很晕,就索性认为是数据的储存出问题了
于是把所有数都输出来后,突然意识到二分只会对数组里的数进行操作,但是对于每个读入的数x,如果x已经被删除,那么我们
需要寻找的就是x的下一位数也就是x+1。
而仔细观察我们会发现每一次读入一个数XX的时候我们只要记录下XX+1,那么当下次我们要删除XX+1的时候,XX+2也会存在(读入的时候就会储存了)
于是我们查询第一个大于等于XX的时候要么答案为XX , 要么为XX+2(XX+1已被删除),这样就能保证题意
同理如果接来下我们要删除XX+2,那么在我们删除XX+2之前我们已将XX+3存入数组中,所以二分查找时就能找到XX+3
这道题还涉及到并查集的维护,这个操作也偏简单,就是维护XX的下一个空位(存在于数组中且未被删除)
1 #include<bits/stdc++.h> 2 #define numm ch - 48 3 using namespace std; 4 template <typename T> 5 void read(T &res) 6 { 7 bool flag = false; 8 char ch; 9 while (!isdigit(ch = getchar())) 10 (ch == '-') && (flag = true); 11 for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm) 12 ; 13 flag && (res = -res); 14 } 15 template <typename T> 16 void Out(T x) 17 { 18 if (x < 0) 19 putchar('-'), x = -x; 20 if (x > 9) 21 Out(x / 10); 22 putchar(x % 10 + '0'); 23 } 24 const int N = 1e6 + 10; 25 int a[N][2]; 26 int ans[N * 3]; 27 int father[N * 3], cnt; 28 int find(int v) 29 { 30 return father[v] == v ? v : father[v] = find(father[v]); 31 } 32 int main() 33 { 34 int ***, n, q, cot = 0, x; 35 read(n); 36 read(q); 37 for (int i = 1; i <= q; i++) 38 { 39 read(***), read(x); 40 ans[++cot] = x - 1; 41 ans[++cot] = x + 1; 42 ans[++cot] = x; 43 a[i][0] = ***, a[i][1] = x; 44 } 45 sort(ans + 1, ans + 1 + cot); 46 int tot = unique(ans + 1, ans + 1 + cot) - ans - 1; 47 for (int i = 0; i <= tot + 1; i++) 48 father[i] = i; 49 for (int i = 1; i <= q; i++) 50 { 51 if (a[i][0] == 1) 52 { 53 int now = lower_bound(ans + 1, ans + 1 + tot, a[i][1]) - ans; 54 int u = find(now); 55 int v = find(now + 1); 56 father[u] = v; 57 } 58 else 59 { 60 int now = lower_bound(ans + 1, ans + 1 + tot, a[i][1]) - ans; 61 int u = find(now); 62 Out(ans[u]); 63 printf("\n"); 64 } 65 } 66 return 0; 67 }
赛后看了网上大佬的官方正解(unordered_map),再次为自己的菜感到自卑
老早前我就有在看关于hash表、hash函数的博客,虽然看懂了它的原理和储存方式,但却不知道具体代码实现的意义
今天特地问了一下大佬,这才知道unordered_map 其实和map差不多,简单讲一下区别:map和set差不多都有内置红黑树,就会自动按key值按字典序从小到大排序 而unordered_map 是采用了哈希函数,没有了排序功能,但是强大的点我们甚至可以用常数的时间进行查找的功能,最坏情况下也才是O(n) 而map的话因为排序了起步都是O(nlogn)
感觉收获还是蛮大的,下面贴下大佬的代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 unordered_map<int,int> mp; 4 int n,m; 5 int find_pre(int x){ 6 if(!mp.count(x)) return x; 7 return mp[x]=find_pre(mp[x]); 8 } 9 int main(){ 10 scanf("%d %d",&n,&m); 11 int op,x; 12 while(m--){ 13 scanf("%d %d",&op,&x); 14 if(op==1) 15 mp[x]=find_pre(x+1); 16 else{ 17 x=find_pre(x); 18 if(x>n) printf("-1\n"); 19 else printf("%d\n",x); 20 } 21 } 22 return 0; 23 }
日后还是要深入学习一下
C. Buy Watermelon
题目链接:https://nanti.jisuanke.com/t/41385
题意:
将一个西瓜切成两半后再判断每半西瓜的重量是不是2的倍数
分析:
很无脑的签到题,直接判断N是不是偶数,再判断N是否>2即可
1 #include <iostream> 2 #include <cstring> 3 #include <stack> 4 #define ll long long 5 #define inf 0x3f3f3f3f 6 using namespace std; 7 int main() 8 { 9 ios::sync_with_stdio(false); 10 int w,flag=0; 11 cin>>w; 12 if(w>2&&w%2==0) 13 cout<<"YES\n"; 14 else 15 cout<<"NO\n"; 16 return 0; 17 }
D. Carneginon
题目链接:https://nanti.jisuanke.com/t/41386
题意:略
分析:
也是一道痕无脑的签到题,唯一恶心的点就是题目太长
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #include<map> 6 #define mm(a,n) memset(a, n, sizeof(a)) 7 using namespace std; 8 #define ll long long 9 template <class T> 10 void read(T &x) 11 { 12 x = 0; 13 char ch = getchar(); 14 while (!isdigit(ch)) 15 ch = getchar(); 16 while (isdigit(ch)) 17 x = x * 10 + ch - '0', ch = getchar(); 18 } 19 const int N = 1e3 + 10; 20 int main() 21 { 22 string T; 23 string S; 24 cin>>T; 25 int n; 26 cin>>n; 27 int len1=T.size(); 28 while(n--) 29 { 30 cin>>S; 31 int len2=S.size(); 32 if(len1>len2) 33 { 34 if(strstr(T.c_str(),S.c_str())) 35 { 36 cout<<"my child!"<<endl; 37 } 38 else 39 cout<<"oh, child!"<<endl; 40 } 41 else if(len1<len2) 42 { 43 if(strstr(S.c_str(),T.c_str())) 44 { 45 cout<<"my teacher!"<<endl; 46 } 47 else 48 cout<<"senior!"<<endl; 49 } 50 else 51 { 52 if(T==S) 53 { 54 cout<<"jntm!"<<endl; 55 } 56 else 57 cout<<"friend!"<<endl; 58 } 59 } 60 return 0; 61 }
E. XKC's basketball team
题目链接:https://nanti.jisuanke.com/t/41387
题意:
给你n个数字,寻找第i个数字后面比i大至少m且距离i最远的数字
分析:
这道题头都快给队友搞晕了
读完题后感觉问题相对来说还是偏简单,a的人也偏多,我就交给队友划个水转手去干B题了
然而等我 Accpet 完B题后已经过了很久然而队友还是没有给我答应,于是心态崩了 ,感觉自
己不止这一场而是最近几场都是一个人在战斗 , 所以最后也没有等时间结束就直接离场吃饭
去了。晚上去教室学习的时候看了下题解和大佬们的代码,基本上都和单调栈/队列 + 二分 、 线段树维护最大区间值 + 二分 离不开关系
然而队友隔了很久只给我发了一个双重for循环0优化的暴力,也是有点莫名其妙,感觉以后这种题还是得自己来,队友有点靠不住
暑期集训的线段树专题好像也有类似题型 , 斟酌了一会后提交一发过
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #include<map> 6 #define mm(a,n) memset(a, n, sizeof(a)) 7 using namespace std; 8 #define ll long long 9 template <class T> 10 void read(T &x) 11 { 12 x = 0; 13 char ch = getchar(); 14 while (!isdigit(ch)) 15 ch = getchar(); 16 while (isdigit(ch)) 17 x = x * 10 + ch - '0', ch = getchar(); 18 } 19 const int N=5e5+5; 20 21 struct node 22 { 23 int l,r,MAX; 24 }tree[N<<2]; 25 26 int n,m; 27 int a[N]; 28 29 void build(int i,int l,int r) 30 { 31 tree[i].l=l,tree[i].r=r; 32 if(l==r) 33 { 34 scanf("%d",&tree[i].MAX); 35 a[l]=tree[i].MAX; 36 return ; 37 } 38 int mid=l+r>>1; 39 build(i<<1,l,mid); 40 build(i<<1|1,mid+1,r); 41 tree[i].MAX=max(tree[i<<1].MAX,tree[i<<1|1].MAX); 42 } 43 44 int query(int i,int l,int r,int v) 45 { 46 if(tree[i].l==tree[i].r) 47 return tree[i].l; 48 int mid=tree[i].l+tree[i].r>>1; 49 if(tree[i<<1|1].MAX>=v) 50 return query(i<<1|1,l,r,v); 51 else if(l<=mid&&tree[i<<1].MAX>=v) 52 return query(i<<1,l,r,v); 53 else 54 return -1; 55 } 56 57 int main() 58 { 59 ios::sync_with_stdio(false); 60 read(n);read(m); 61 build(1,1,n); 62 int ans; 63 for(int i=1;i<=n;i++) 64 { 65 if(i==n) 66 ans=-1; 67 else 68 ans=query(1,i+1,n,a[i]+m); 69 if(ans!=-1) 70 ans-=i+1; 71 if(i == n) 72 cout<<ans<<endl; 73 else cout<<ans<<" "; 74 } 75 return 0; 76 }
G. Colorful String
题目链接:https://nanti.jisuanke.com/t/41389
题意:
给定一个字符串,将该字符串的所有回文串的不重复出现的字母贡献记为1,计算总贡献值
分析:
真是晕了。。。
读完题目感觉是自己能A的题,因为不久前刚粗略的学习了一手回文自动机,然而并没有什么锤子用
思考了快一个小时又想着用马拉车算法搞一波 ,but很快发现想法是错的,于是最后就很尴尬的把时
间浪费在这道题身上
果然自己对字符串的处理还是远远不够的。
上次找涛哥教AC自动机也不知道他什么时候有空教,感觉他最近也挺忙的。。。
我想主要还是要是要我多自学点吧,加油加油加油