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

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

  • 相关阅读:
    关于leetcode中链表中两数据相加的程序说明
    数字波束合成的一些理解
    我们可以通过结构和指针构建强大的数据结构
    递归的理解
    dm9000网口收发控制以及mac地址过滤设置
    dsp28377控制DM9000收发数据——第三版程序,通过外部引脚触发来实现中断接受数据,优化掉帧现象
    dsp28377控制DM9000收发数据——第二版程序,能够实现手术功能,但是容易掉帧;使用读取中断寄存器的方式判断中断
    dsp28377控制DM9000收发数据
    树形导航栏(折叠)(jquery)
    折叠(树形导航栏)
  • 原文地址:https://www.cnblogs.com/StarRoadTang/p/11484151.html
Copyright © 2011-2022 走看看