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

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 fuck, n, q, cot = 0, x;
35     read(n);
36     read(q);
37     for (int i = 1; i <= q; i++)
38     {
39         read(fuck), read(x);
40         ans[++cot] = x - 1;
41         ans[++cot] = x + 1;
42         ans[++cot] = x;
43         a[i][0] = fuck, 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 }
View Code

赛后看了网上大佬的官方正解(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 }
View Code

日后还是要深入学习一下

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

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

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

 

G. Colorful String

题目链接:https://nanti.jisuanke.com/t/41389

题意:

给定一个字符串,将该字符串的所有回文串的不重复出现的字母贡献记为1,计算总贡献值

分析:

真是晕了。。。

读完题目感觉是自己能A的题,因为不久前刚粗略的学习了一手回文自动机,然而并没有什么锤子用

思考了快一个小时又想着用马拉车算法搞一波 ,but很快发现想法是错的,于是最后就很尴尬的把时

间浪费在这道题身上

果然自己对字符串的处理还是远远不够的。

上次找涛哥教AC自动机也不知道他什么时候有空教,感觉他最近也挺忙的。。。

我想主要还是要是要我多自学点吧,加油加油加油

原文地址:https://www.cnblogs.com/StarRoadTang/p/11484151.html