zoukankan      html  css  js  c++  java
  • 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自动机也不知道他什么时候有空教,感觉他最近也挺忙的。。。

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

  • 相关阅读:
    win10 UWP button
    内网分享资源
    内网分享资源
    CF724F Uniformly Branched Trees
    win10 UWP FlipView
    win10 UWP FlipView
    win10 UWP FlipView
    搭建阿里云 centos mysql tomcat jdk
    搭建阿里云 centos mysql tomcat jdk
    win10 UWP 申请微软开发者
  • 原文地址:https://www.cnblogs.com/StarRoadTang/p/11484151.html
Copyright © 2011-2022 走看看