zoukankan      html  css  js  c++  java
  • Yandex Algorithm 2018 Warmup

    A. Time Throught The Glass

    题意:对于一个时钟,分针和秒针一定会在整数位置。下面告诉你这个时钟关于中垂线对称后的读数,让你输出它的真实读数。

    观察:如果镜像读数是h,m的话,真实读数就是(12-h)%12, (60-m)%60。

    code:

     1 /*
     2  by skydog
     3  */
     4 #include <iostream>
     5 #include <cstdio>
     6 #include <vector>
     7 #include <utility>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <map>
    12 #include <set>
    13 #include <stack>
    14 #include <queue>
    15 #include <deque>
    16 #include <cassert>
    17 #include <list>
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 typedef unsigned long long ull;
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 
    27 int main()
    28 {
    29     int a, b;
    30     scanf("%d %d", &a, &b);
    31     printf("%d %d
    ", (12-a)%12, (60-b)%60);
    32 
    33 }
    View Code

    B. Palindromic Feature

    题意:给你一个长度不超过2e5的小写字母串,让你找出一个长度不小于2的最短回文字串。如果最短的由多个,取字典序最小的。无解输出-1。

    观察:如果有解的话,答案一定是长度为2或者长度为3的串。暴力检查就好

    code:

     1 /*
     2  by skydog
     3  */
     4 #include <iostream>
     5 #include <cstdio>
     6 #include <vector>
     7 #include <utility>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <map>
    12 #include <set>
    13 #include <stack>
    14 #include <queue>
    15 #include <deque>
    16 #include <cassert>
    17 #include <list>
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 typedef unsigned long long ull;
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 bool work(const string &str)
    27 {
    28     for (int i = 0; i < str.length(); ++i)
    29         if (str[i] != str[str.length()-i-1])
    30             return false;
    31     return true;
    32 }
    33 string check(const string &str, int len)
    34 {
    35     string ret = "";
    36     for (int i = 0; i+len <= str.length(); ++i)
    37     {
    38         auto tmp = str.substr(i, len);
    39         if (!work(tmp))
    40             continue;
    41         if (ret.empty() || ret > tmp)
    42             ret = tmp;
    43     }
    44     return ret;
    45 }
    46 int main()
    47 {
    48     string str;
    49     cin >> str;
    50     string ans = check(str, 2);
    51     if (ans.empty())
    52         ans = check(str, 3);
    53     if (ans.empty())
    54         cout << "-1
    ";
    55     else
    56         cout << ans << '
    ';
    57 }
    View Code

    WA:一开始没有看清楚题意,还以为是要求字典序最小的回文子串,就开始找后缀家族和回文家族的模版。但是看到这么多人过了,重新读了下题目,才意识到是水题。

    C. Divide Them All

    题意:给你不超过1e5个图形,圆或者长方形。圆心或者长方形顶点,都是整点。问你是否存在一条直线,可以将每一个图形的面积平分。

    观察:其实就是问所有图形的重心是否在一条直线上。圆的重心就是圆心,长方形重心是对角线的中点。这里不知道double会不会有精度问题,所以我就把坐标都乘了2。

    code:

     1 /*
     2  by skydog
     3  */
     4 #include <iostream>
     5 #include <cstdio>
     6 #include <vector>
     7 #include <utility>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <map>
    12 #include <set>
    13 #include <stack>
    14 #include <queue>
    15 #include <deque>
    16 #include <cassert>
    17 #include <list>
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 typedef unsigned long long ull;
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 
    27 vector<ii> v;
    28 ii get()
    29 {
    30     int t;
    31     scanf("%d", &t);
    32     if (t == 0)
    33     {
    34         ii ret;
    35         
    36         scanf("%*d %d %d", &ret.first, &ret.second);
    37         ret.first *= 2;
    38         ret.second *= 2;
    39         return ret;
    40     }
    41     else
    42     {
    43         int x[4], y[4];
    44         for (int i = 0; i < 4; ++i)
    45             scanf("%d %d", x+i, y+i);
    46         return mp(x[0]+x[2], y[0]+y[2]);
    47     }
    48 }
    49 bool solve()
    50 {
    51     sort(v.begin(), v.end());
    52     v.resize(unique(v.begin(), v.end())-v.begin());
    53     if (v.size() <= 2)
    54         return true;
    55     for (int i = 2; i < v.size(); ++i)
    56     {
    57         int x1 = v[1].first-v[0].first, y1 = v[1].second-v[0].second;
    58         int x2 = v[i].first-v[0].first, y2 = v[i].second-v[0].second;
    59         if (x1*y2 != x2*y1)
    60             return false;
    61     }
    62     return true;
    63 }
    64 int main()
    65 {
    66     int n;
    67     scanf("%d", &n);
    68     for (int i = 1; i <= n; ++i)
    69     {
    70         v.pb(get());
    71     }
    72     puts(solve()?"Yes":"No");
    73 }
    View Code

    WA:忘记去重了,导致判断出错。看来blind提交还是有一定风险的。

    D. Interview Task

    题意:给了你一个递归式。f[1] = {1, 1}。f[i] = 在f[i-1]的基础上,将f[i-1]中任意两个相邻的元素的和插入两个相邻元素中间。f[2] = {1, 1+1, 1} = {1, 2, 1};f[3] = {1, 1+2, 2, 2+1, 1} = {1, 3, 2, 3, 2, 1}。给你一个正整数n (n <= 1e13),问你在f[n]中有几个n。

    观察:推了一会没有推出来,决定打表找规律。看了半天,先发现对于一个prime p, ans[p] = p-1。好像有点意思,然后发现除了n=1的情况,ans[n] = phi(n)。

    code:

     1 /*
     2  by skydog
     3  */
     4 #include <iostream>
     5 #include <cstdio>
     6 #include <vector>
     7 #include <utility>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <map>
    12 #include <set>
    13 #include <stack>
    14 #include <queue>
    15 #include <deque>
    16 #include <cassert>
    17 #include <list>
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 typedef unsigned long long ull;
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 ll solve(ll n)
    27 {
    28     if (n == 1)
    29         return 2;
    30     ll ret = n;
    31     for (ll i = 2; i*i <= n; ++i)
    32         if (n%i==0)
    33         {
    34             ret -= ret/i;
    35             while (n%i == 0)
    36                 n/=i;
    37         }
    38     if (n != 1)
    39         ret -= ret/n;
    40     return ret;
    41 }
    42 
    43 int main()
    44 {
    45     ll n;
    46     scanf("%lld", &n);
    47     printf("%lld
    ", solve(n));
    48 }
    View Code

    WA:现场的时候应该比较有勇气的直接打表找规律,尤其是看到这么多人过的情况。事后诸葛亮的话,输入的n不超过1e13,可能暗示了要用sqrt(n)的方法来解。

    回顾:看到CF上有人提示,数列其实是Stern-Brocot Tree的分母。比赛的时候有想到这个东西,但是对它不是特别了解,连名字都不记得(一年半以前usaco的某一道题用过)。如果了解这个知识点的话,答案就比较明了了。f[n]就是Stern-Brocot Tree第n层的分母序列,f[n]中n的个数就是[0, 1]内,以n为分母的最简分数的个数。n=1时有两个最简分数0/1 and 1/1。其他情况,以n为底的最简分数的个数就等于分子小于n且与n互质的个数,也就是phi(n)。

    E. Backup

    题意:给你一颗大小不超过1e5的有根树,还有最大负载量k(k >= 2)。起初每个点都有1个单位的负载。每一次操作你可以删一个点,如果这个点的parent还没被删掉,那么删除该点会使parent的负载加1。当某个点的负载到达k时,下一秒必须删除这个点。当根节点被删除时,停止操作。问你最多可以进行几次操作,根节点被删除的操作不算。

    观察:观察过样例之后才想清楚可能的情况,对于一个节点cur,有三种处理方式:(0) 直接先把cur删掉,之后再删cur的孩子们。(1)最后删cur,在cur的孩子们中尽量删,删完k-2个后,最后再删一个孩子,刚好使得cur的负载达到k。注意,根节点必须是这种操作。(2)保证cur不被删除。

       下面我们考虑一下每种方案的最大操作数该怎么求,先令第i种方案的最大操作数dp[cur][i]。

      (0)可以想到,在这种方案下的最大操作数就是cur所在的子树大小,具体操作就是按照从cur开始的bfs序删点,即删掉cur,再删掉cur的孩子们,再删掉cur的孩子的孩子们,以此类推。dp[cur][0] = 1 + sum(dp[child of cur][0])。

      (1)一开始肯定是删k-2个孩子,然后再删一个孩子,剩下的孩子都不删。dp[cur][1] = 1 + max{sum(d[前k-2个孩子][0]) + d[最后删的孩子][1] + sum(d[没被删的孩子][2])}。

      (2)删掉k-2个孩子,剩下的孩子都不删。dp[cur][2] = max{sum(d[前k-2个孩子][0]) + sum(d[没被删的孩子][2])}

       最后的答案就是d[根节点][1] - 1。这里减1是因为题目要求根节点不算在答案中。

       比较容以看出, dp[cur][0]就是子树大小,比较好求。但对于其他两类方案,dp的值和选孩子的方案有关。我们先考虑dp[cur][2]的求法。我们要取k-2个孩子的dp[nxt][0]和剩下孩子的dp[nxt][2],是一个经典的排序贪心问题。即给n个元素,每个元素有a[i], b[i]两个值,问你取x个元素的a[i]和剩下n-x个元素的b[i],最大的和是多少。解决方法是将元素按照a[i]-b[i]的顺序从大到小排序,取前x元素的a[i],后n-x个元素的b[i]就好了。所以为了求dp[cur][2],我们只需要将cur所有孩子按照dp[nxt][0]-dp[nxt][1]排一下降序,取前k-2个孩子的dp[nxt][0]和剩下孩子的dp[nxt][2]就好。

       dp[cur][1]的求法也是类似,先将cur的孩子按照上述方法排序之后,枚举最后被删的孩子是哪一个,更新答案。这里可以先预处理出前缀和和后缀和,枚举被删除的孩子的时候可以快速更新答案。

    code:

     1 /*
     2  by skydog
     3  */
     4 #include <iostream>
     5 #include <cstdio>
     6 #include <vector>
     7 #include <utility>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <map>
    12 #include <set>
    13 #include <stack>
    14 #include <queue>
    15 #include <deque>
    16 #include <cassert>
    17 #include <list>
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 typedef unsigned long long ull;
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 const int maxn = 1e5+1;
    27 vector<int> g[maxn];
    28 int n, k, p[maxn];
    29 
    30 int d[maxn][3];
    31 /*
    32  0 for first pick
    33  1 for last pick
    34  2 for not pick
    35  */
    36 
    37 bool cmp(int a, int b)
    38 {
    39     return d[a][0]-d[a][2] > d[b][0]-d[b][2];
    40 }
    41 void dfs(int cur)
    42 {
    43     d[cur][0] = 1;
    44     for (auto nxt : g[cur])
    45     {
    46         dfs(nxt);
    47         d[cur][0] += d[nxt][0];
    48     }
    49     if (g[cur].size() < k-1)
    50     {
    51         d[cur][1] = d[cur][0];
    52         d[cur][2] = d[cur][0]-1;
    53     }
    54     else
    55     {
    56         //2 for not pick
    57         //1 for last pick
    58         sort(g[cur].begin(), g[cur].end(), cmp);
    59         int front = 0, back = 0;
    60         for (int i = 0; i < k-1; ++i)
    61             front += d[g[cur][i]][0];
    62         for (int i = k-1; i < g[cur].size(); ++i)
    63             back += d[g[cur][i]][2];
    64         int ans = 0;
    65         for (int i = 0; i < k-1; ++i)
    66         {
    67             int tmp = front+back-d[g[cur][i]][0]+d[g[cur][i]][1];
    68             if (tmp > ans)
    69                 ans = tmp;
    70         }
    71         front -= d[g[cur][k-2]][0];
    72         back += d[g[cur][k-2]][2];
    73         for (int i = k-1; i < g[cur].size(); ++i)
    74         {
    75             int tmp = front+back-d[g[cur][i]][2]+d[g[cur][i]][1];
    76             if (tmp > ans)
    77                 ans = tmp;
    78         }
    79         d[cur][1] = 1+ans;
    80         d[cur][2] = front+back;
    81     }
    82 }
    83 int main()
    84 {
    85     int T;
    86     scanf("%d", &T);
    87     for (int kase = 1; kase <= T; ++kase)
    88     {
    89         scanf("%d %d", &n, &k);
    90         for (int i = 1; i <= n; ++i) g[i].clear();
    91         for (int i = 1; i <= n-1; ++i)
    92             scanf("%d", p+i), g[p[i]].pb(i);
    93         dfs(n);
    94         printf("%d
    ", d[n][1]-1);
    95         
    96     }
    97 }
    View Code

     

    F. Lying Processors

    题意:在一个n*m的主板上,每个位置都有一个处理器,n<=7, m <= 100。处理器分两种,一定说实话的和一定说谎话的。对每一个处理其都问了一个相同的问题:你相邻的处理器中是不是既有说谎的也有说实话的?每一个处理器都回答了是。问你最少可能有多少个说谎的处理器。

    猜测:看数据范围和问题模式,感觉会是个轮廓线dp。而且询问的种类不是很多,看似可以打表提交。

    观察:本来想状态可能有很多种,但想清楚了之后,只用0,1代表说实话的和说谎话的就好了。

      首先有个独立于dp之外的观察,就是,所有人都说谎一定可行。而且对于一个说谎的人,他要么周围都是说实话的,即这个说谎者孤立;要么他周围都是说谎话的,以此类推可以推广到所有人都是说谎话的。全都是说谎话的人,答案是n*m,所以这就是答案的上限,之后我们只需要考虑说谎者孤立的情况。但是由于一开始想法不是很清晰,而且数据范围比较小,又存在本地打表的可能,接下来的写法并没有用到这一点。

      首先预处理m=1的情况:暴力枚举2^n种可能,检查状态是否合法,如果合法,更新答案。

      对于m >= 2的情况,暴力枚举前两行的所有可能,(2^n) * (2^n),之后维护过去的两行,进行状态转移。最后,再暴力枚举最后两行所有的可能,用合法的状态更新答案。

    code:

      1 /*
      2  by skydog
      3  */
      4 #include <iostream>
      5 #include <cstdio>
      6 #include <vector>
      7 #include <utility>
      8 #include <algorithm>
      9 #include <cmath>
     10 #include <cstring>
     11 #include <map>
     12 #include <set>
     13 #include <stack>
     14 #include <queue>
     15 #include <deque>
     16 #include <cassert>
     17 #include <list>
     18 using namespace std;
     19 typedef long long ll;
     20 typedef pair<int, int> ii;
     21 typedef pair<ll, ll> l4;
     22 typedef unsigned long long ull;
     23 #define mp make_pair
     24 #define pb push_back
     25 
     26 void print(int bit, int n)
     27 {
     28     for (int i = 0; i < n; ++i)
     29         putchar('0'+((bit>>i)&1));
     30 }
     31 
     32 const int N = 7;
     33 const int maxs = 1<<N;
     34 int bit[maxs];
     35 
     36 const int maxm = 100;
     37 int d[maxm][N][maxs][maxs];
     38 int pre[maxm][N][maxs][maxs];
     39 int n, m;
     40 //0 for honest, 1 for liar
     41 bool check_single(int mask, int n)
     42 {
     43     for (int i = 0; i < n; ++i)
     44         if ((mask>>i)&1)
     45         {
     46             // for liar
     47             if (i-1 >= 0 && i+1 < n && ((mask>>i-1)&1)^((mask>>i+1)&1))
     48                 return false;
     49         }
     50         else
     51         {
     52             if (i == 0 || i == n-1 || ((mask>>i-1)&1)==((mask>>i+1)&1))
     53                 return false;
     54         }
     55     //print(mask, n);
     56     //cerr << " " << bit[mask] << "  work
    ";
     57     return true;
     58 }
     59 int solve_m(int n)
     60 {
     61     int ans = n*2;
     62     for (int i = 0; i < (1<<n); ++i)
     63     {
     64         if (check_single(i, n) && bit[i] < ans)
     65             ans = bit[i];
     66     }
     67     assert(ans <= n);
     68     return ans;
     69 }
     70 bool check_first_two(int a, int b, int n)
     71 {
     72     for (int i = 0; i < n; ++i)
     73         if ((a>>i)&1)
     74         {
     75             //liar
     76             bool cnt[2]={0, 0};
     77             if (i-1>=0)
     78                 cnt[(a>>i-1)&1] = true;
     79             if (i+1<n)
     80                 cnt[(a>>i+1)&1] = true;
     81             cnt[(b>>i)&1] = true;
     82             if (cnt[1]&&cnt[0])
     83                 return false;
     84         }
     85         else
     86         {
     87             //liar
     88             bool cnt[2]={0, 0};
     89             if (i-1>=0)
     90                 cnt[(a>>i-1)&1] = true;
     91             if (i+1<n)
     92                 cnt[(a>>i+1)&1] = true;
     93             cnt[(b>>i)&1] = true;
     94             if (!cnt[1] || !cnt[0])
     95                 return false;
     96         }
     97     swap(a, b);
     98     for (int i = 0; i < n; ++i)
     99         if ((a>>i)&1)
    100         {
    101             //liar
    102             bool cnt[2]={0, 0};
    103             if (i-1>=0)
    104                 cnt[(a>>i-1)&1] = true;
    105             if (i+1<n)
    106                 cnt[(a>>i+1)&1] = true;
    107             cnt[(b>>i)&1] = true;
    108             if (cnt[1]&&cnt[0])
    109                 return false;
    110         }
    111     return true;
    112 }
    113 bool check_last_two(int a, int b, int n)
    114 {
    115     return check_first_two(b, a, n);
    116 }
    117 
    118 void init_bit(int n)
    119 {
    120     for (int i = 1; i < (1<<n); ++i)
    121         bit[i] = bit[i>>1] + (i&1);
    122 }
    123 void dfs(int tar, int dep, int n)
    124 {
    125     int old = tar;
    126     if (dep > 1)
    127     {
    128         tar = pre[dep][0][tar%(1<<n)][tar>>n];
    129         for (int i = n-1; i >= 1; --i)
    130             tar = pre[dep-1][i][tar%(1<<n)][tar>>n];
    131         dfs(tar, dep-1, n);
    132     }
    133     if (dep == 1)
    134         print(tar%(1<<n), n), puts("");
    135     print(old>>n, n);
    136 
    137     puts("");
    138 }
    139 int solve(int n, int m)
    140 {
    141     init_bit(n);
    142     if (m == 1)
    143         return solve_m(n);
    144     //brute first two row
    145     memset(d, 7, sizeof(d));
    146     const int inf = n*m*2;
    147     for (int i = 0; i < (1<<n); ++i)
    148         for (int j = 0; j < (1<<n); ++j)
    149             if (check_first_two(i, j, n))
    150                 d[1][0][i][j] = bit[i]+bit[j];
    151     //check middle
    152     for (int r = 1; r < m-1; ++r)
    153         for (int w = 0; w < n; ++w)
    154         {
    155             int nr = r, nw = w+1;
    156             if (nw == n)
    157                 nw = 0, ++nr;
    158             for (int i = 0; i < (1<<n); ++i)
    159                 for (int j = 0; j < (1<<n); ++j)
    160                     if (d[r][w][i][j] < inf)
    161                     {
    162                         //consider first bit of j
    163                         if (j&1)
    164                         {
    165                             //liar
    166                             int pick = i&1;
    167                             if (pick && w != 0 && ((j>>n-1)&1) != 1)
    168                                 continue;
    169                             int &nxt = d[nr][nw][(i>>1)|(1<<n-1)][(j>>1)|((i&1)<<n-1)];
    170                             int &prev = pre[nr][nw][(i>>1)|(1<<n-1)][(j>>1)|((i&1)<<n-1)];
    171                             if (nxt > d[r][w][i][j]+(i&1))
    172                                 nxt = d[r][w][i][j]+(i&1), prev = (j<<n)|i;
    173                         }
    174                         else
    175                         {
    176                             bool cnt[2] = {0, 0};
    177                             cnt[i&1] = true;
    178                             if (w != n-1)
    179                                 cnt[(j>>1)&1] = true;
    180                             if (w != 0)
    181                                 cnt[(i>>n-1)&1] = true;
    182                             if (cnt[0])
    183                             {
    184                                 if (w != 0 && ((j>>n-1)&1) != 0)
    185                                     goto haha;
    186                                     
    187                                 int &nxt = d[nr][nw][(i>>1)][(j>>1)|(1<<n-1)];
    188                                 int &prev = pre[nr][nw][(i>>1)][(j>>1)|(1<<n-1)];
    189                                 if (nxt > d[r][w][i][j]+1)
    190                                     nxt = d[r][w][i][j]+1, prev = (j<<n)|i;
    191                             }
    192                             
    193                         haha: if (cnt[1])
    194                             {
    195                                 int &nxt = d[nr][nw][(i>>1)][(j>>1)|(0<<n-1)];
    196                                 int &prev = pre[nr][nw][(i>>1)][(j>>1)|(0<<n-1)];
    197                                 if (nxt > d[r][w][i][j])
    198                                     nxt = d[r][w][i][j], prev = (j<<n)|i;
    199                             }
    200                                 
    201                         }
    202                     }
    203         }
    204     //check last two row
    205     int ans = n*m;
    206     int tar = 0;
    207     for (int i = 0; i < (1<<n); ++i)
    208         for (int j = 0; j < (1<<n); ++j)
    209             if (check_last_two(i, j, n) && d[m-1][0][i][j] < ans)
    210             {
    211                 ans = d[m-1][0][i][j];
    212                 tar = (j<<n)|i;
    213             }
    214     //dfs(tar, m-1, n);
    215     return ans;
    216                 
    217 }
    218 void check()
    219 {
    220     for (int i = 1; i <= 7; ++i)
    221         for (int j = 1; j <= 7; ++j)
    222             assert(solve(i, j) == solve(j, i));
    223 }
    224 int main()
    225 {
    226    // check();
    227     
    228     while (~scanf("%d %d", &n, &m))
    229         printf("%d
    ", solve(n, m));
    230 }
    View Code

    上面的代码写了很多重复的段落,而且有些地方写的复杂了,没有用到黑点孤立的性质。

    不知道根据黑点孤立,可不可以推出一个封闭的式子计算答案。

  • 相关阅读:
    generator
    JS 中 apply 、call 、bind的详解
    前端面试题(24-js)
    JS原型链深入了解
    Java12新特性
    Java11-ZGC
    Java11新特性
    Java10新特性
    Java9新特性
    CF1385E【Directing Edges】 (拓扑排序)
  • 原文地址:https://www.cnblogs.com/skyette/p/8443588.html
Copyright © 2011-2022 走看看