zoukankan      html  css  js  c++  java
  • Wannafly 挑战赛 9

    比赛链接:https://www.nowcoder.com/acm/contest/71#question

    AC:A(-4),C(-3)

    A. 找一找

    题意:给你一个长度为n(n <= 1e5) 的正整数序列a[] (a[i] <= 1e5)。问你有多少个i满足, 存在某个j和大于1的正整数k(k>1), 使得a[j] = a[i]*k。

    观察:a[i]的范围比较小,可以对[1, 1e5]之内每一种数进行搜索,令上界为N,这里N=1e5,复杂度为 O(N/1+N/2+...+N/N) = O(N * log(N))。

    方法:用一个int vis[1e5+1] 记录下每个数出现的次数就行了

    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 
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 
    23 #define mp make_pair
    24 #define pb push_back
    25 #include<cstdio>
    26 using namespace std;
    27 
    28 const int maxn = 1e6+1;
    29 int vis[maxn] = {0};
    30 inline int get(int a)
    31 {
    32     for (int j = 2*a; j < maxn; j += a)
    33         if (vis[j])
    34             return true;
    35     return false;
    36 }
    37 int main()
    38 {
    39     memset(vis, 0, sizeof(vis));
    40     int n;
    41     scanf("%d", &n);
    42     for (int i = 0; i < n; ++i)
    43     {
    44         int x;
    45         scanf("%d", &x);
    46         vis[x] += 1;
    47     }
    48     int ans = 0;
    49     for (int i = 1; i < maxn; ++i)
    50         if (vis[i])
    51             ans += vis[i]*get(i);
    52     printf("%d
    ", ans);
    53 }
    View Code

     WA:没有读清楚题目。

    B. 数一数

    题意:给你n (n <= 1e6) 个总长度不超过 2e6的string s[1...n],定义f(P, T) 为P在T中的出现次数。让你对于每一个i 属于[1, n], 求出 ans[i] = product of {f(s[i], s[j]), j = 1...n} mod 998244353。

    观察:既然是求乘积,可以想到如果相乘的项中有一个为0,那么结果就是0了。那么很明显的是,如果一个P的长度大于T,或者P和T长度相等但是内容不同,那么f(P, T) 为0,所以乘积为0。所以只需要考虑长度最小一个的串mini,其他的串中,如果长度比mini.length()大,或者是长度等于mini.length()但是和mini不相同,那么答案是0。

    方法:找一个长度最小的串mini,用kmp求出mini的答案result。输出结果s[i]的结果时,如果s[i]和mini相同,那么输出result,否则输出0。

    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 
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 const int maxn = 2e6+1;
    27 const int mod = 998244353;
    28 string str[maxn], mini;
    29 int n;
    30 int f[maxn];
    31 int kmp(const string &T)
    32 {
    33     int ret = 0;
    34     for (int i = 0, j = 0; i < T.length(); ++i)
    35     {
    36         while (j != -1 && T[i] != mini[j])
    37             j = f[j];
    38         ++j;
    39         if (j == mini.size())
    40         {
    41             ++ret;
    42             j = f[j];
    43         }
    44     }
    45     return ret;
    46 }
    47 int main()
    48 {
    49     ios::sync_with_stdio(false);
    50     cin.tie(0);
    51     cin >> n;
    52     for (int i = 0; i < n; ++i)
    53         cin >> str[i];
    54     mini = str[0];
    55     for (int i = 0; i < n; ++i)
    56         if (str[i].length() < mini.length()) mini = str[i];
    57     for (int j = f[0] = -1, i = 0; i < mini.length(); ++i, ++j)
    58     {
    59         while (j != -1 && mini[i] != mini[j])
    60             j = f[j];
    61         f[i+1] = j+1;
    62     }
    63     int ans = 1;
    64     for (int i = 0; i < n; ++i)
    65     {
    66         ans = (ll) ans * kmp(str[i]) % mod;
    67         if (!ans)
    68             break;
    69     }
    70     
    71     for (int i = 0; i < n; ++i)
    72         if (str[i] == mini)
    73             cout << ans << '
    ';
    74         else
    75             cout << 0 << '
    ';
    76     
    77 }
    View Code

    WA: 没有想到f(P, T) 为0的条件。

    C. 列一列

    题意:考虑fibonacci数列,从第一项开始,A[1] = 1, A[2] = 2, A[k] = A[k-1]+A[k-2] when k >= 3。下面给你不超过三十个A[k],k保证不超过1e5(k<=1e5)。让你输出对应的k。

    观察:本来是想fibonacci数列是有循环节的,找一个循环节很大的质数模一模来判重,但有几个问题:(1)对一个大整数取mod,在不使用大整数库的情况下,手写比较麻烦;(2)虽然循环节很大,但是一个循环节内,是可能存在mod后余数相等的情况的,即出现冲突。(1)第一个问题,我就考虑直接对10^k取模,方便很多,把大整数按照一个string读入,然后取它后k位就好了。(2)第二个问题,我在本机试了一下,发现mod 10^10时,刚好没有冲突。于是就去了mod 10^10

    方法:暴力枚举计算数列前1e5项mod 10^10的值,放到一个map里。令数列项数上界为N,此处N=1e5,单次询问的时间复杂度时O(length of string + log(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 
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 
    27 ll get(string s, int k)
    28 {
    29     int len = s.length();
    30     ll ret = 0;
    31     for (int i = max(0, len-k); i < len; ++i)
    32     {
    33         ret = 10*ret + s[i]-'0';
    34     }
    35     return ret;
    36 }
    37 ll ten(int k)
    38 {
    39     return k?10*ten(k-1):1;
    40 }
    41 map<ll, int> ma;
    42 const int SZ = 1e5;
    43 bool try_mod(ll mod)
    44 {
    45     vector<ll> f({1%mod, 2%mod});
    46     ma.clear();
    47     while (f.size() < SZ)
    48     {
    49         int sz = f.size();
    50         f.pb((f[sz-1]+f[sz-2])%mod);
    51     }
    52     for (int i = 0; i < f.size(); ++i)
    53         ma[f[i]] = i+1;
    54     
    55     cerr << "f.size() = " << f.size() << endl;
    56     cerr << "ma.size() = " << ma.size() << endl;
    57     return f.size() == ma.size();
    58 }
    59 
    60 int main()
    61 {
    62     int k = 10;
    63     assert(try_mod(ten(k)));
    64     string s;
    65     while (cin >> s)
    66     {
    67         ll n = get(s, k);
    68         assert(ma.count(n));
    69         cout << ma[n] << endl;
    70     }
    71     return 0;
    72 }
    View Code

     WA:应该用map,但是傻了用了vector加lower_bound。

    D. 造一造

    题意:将1到n (n<=1e6) 依次插入一个stack里,某一时刻若stack非空,你可以弹出stack中的元素,最后要使stack变空。给你m,k ( m, k <= 1e6),问要求第m个元素入stack之后,stack中恰好有k个元素的出栈入栈顺序有几种。答案mod 1e9+7。

    观察:入栈顺序已经固定了嘛,所以,如果把1当作某个元素入栈,0当作某个元素出栈,我们需要确定了一个长度为2*n的01串,并且使任意时刻1出现的次数不小于0出现的次数,听起来很Catalan。所以就是要求有几个长度为2*n的Catalan序列,使得第m个1出现时(包含第m个1),1的个数 减 0的个数 等于k。

    然后就想解决一个子问题solve(one, zero)即满足任意前缀中1的个数要大于等于0的个数,且有one个1,zero个0的方法数。答案就是solve(m-1, m-k) * solve(n+k-m, n-m) 。为什么是这个呢,因为我们要求放第m个1的时候,前面的元素合法,即前面有m-1个1和m-k个0,方法数是solve(m-1, m-k)。考虑放完m之后的位置,要继续放n-m个1和n-(m-k) = n+k-m个0,那么对于后缀,从后往前看,后缀合法的条件是任意一个后缀中0的个数不小于1的个数,即solve(n+k-m, n-m)。

    那么solve(one, zero) 要怎么求呢?我们可以考虑Catalan数的证明。Catalan数等于将n个0和n个1排列,使得对于每个前缀,1的个数都不少于0的个数的方法数,计作C(n),C(n) = c(2n, n) - c(2n, n+1) = c(2n, n)/(n+1),其中小写字母c代表组合数。

    在这里贴一个数形结合的证明 http://blog.miskcoo.com/2015/07/catalan-number。还用另一种本质相同的证明,简单说一下。将n个0和n个1排成一排的方法数为c(2n, n),即从2n个位置中选出n个放1。下面考虑从这c(2n, n)中除去不符合我们Catalan性质的序列。那么对于每一个不合法的序列,一定存在左边第一个位置,0的个数zero = (1的个数one)+1。然后我们将这前(zero+one)个数中1和0交换,得到了一个由(n+1)个1和(n-1)个0组成的排列。同时可以发现,对于任何一个由(n+1)个1和(n-1)个0组成的排列,我们一定能找到左边第一个位置,使得 1的个数one = (0的个数zero) + 1。那么将这前(one+zero)个数中的1和0交换,我们得到了一个由n个1和n个0组成的不合法排列。所以不合法序列 和 由(n+1)个1和(n-1)个0组成的排列 是一一对应的,数量也相同,不合法序列数 = 由(n+1)个1和(n-1)个0组成的排列的个数 = c(2n, n+1)。所以C(n) = c(2n, n) - c(2n, n+1)。

    同理,我们可以推出solve(one, zero) = c(one+zero, one) - c(one+zero, one+1),即答案。

    方法:预处理阶乘f[n]和阶乘的逆inv[n],快速求出组合数,然后按照上述公式输出答案。

    坑:组合数的底数可以达到2*n,即2e6。

    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 
    18 using namespace std;
    19 typedef long long ll;
    20 typedef pair<int, int> ii;
    21 typedef pair<ll, ll> l4;
    22 
    23 #define mp make_pair
    24 #define pb push_back
    25 
    26 const int mod = 1e9+7;
    27 const int maxn = 2e6+1;
    28 ll f[maxn], inv[maxn];
    29 inline ll power(ll base, ll p)
    30 {
    31     ll ret = 1;
    32     while (p)
    33     {
    34         if (p&1) ret = ret * base % mod;
    35         base = base * base % mod;
    36         p >>= 1;
    37     }
    38     return ret;
    39 }
    40 inline void init()
    41 {
    42     f[0] = 1;
    43     for (int i = 1; i < maxn; ++i) f[i] = f[i-1] * i % mod;
    44     inv[maxn-1] = power(f[maxn-1], mod-2);
    45     for (int i = maxn-1; i >= 1; --i) inv[i-1] = inv[i] * i % mod;
    46 }
    47 ll cb(ll a, ll b)
    48 {
    49     if (a < b || b < 0) return 0;
    50     return f[a] * inv[b] % mod * inv[a-b] % mod;
    51 }
    52 ll solve(ll one, ll zero)
    53 {
    54     ll all = one+zero;
    55     ll ret = (cb(all, one)-cb(all, one+1))%mod;
    56     if (ret < 0) ret += mod;
    57     return ret;
    58 }
    59 int main()
    60 {
    61     init();
    62     int T;
    63     scanf("%d", &T);
    64     for (int kase = 1; kase <= T; ++kase)
    65     {
    66         int n, m, k;
    67         scanf("%d %d %d", &n, &m, &k);
    68         //prefix (m-1) one and (m-k) zero
    69         //thus suffix n+k-m zero and n-m one
    70         printf("%lld
    ", solve(m-1, m-k)*solve(n+k-m, n-m)%mod);
    71     }
    72 }
    View Code

     WA:没有想到由Catalan的证明扩展到一般情况。赛中没有细想,赛后逐步推导发现答案很简单。

    E. 组一组

    F. 导一导

  • 相关阅读:
    PAT Advanced 1067 Sort with Swap(0, i) (25分)
    PAT Advanced 1048 Find Coins (25分)
    PAT Advanced 1060 Are They Equal (25分)
    PAT Advanced 1088 Rational Arithmetic (20分)
    PAT Advanced 1032 Sharing (25分)
    Linux的at命令
    Sublime Text3使用指南
    IntelliJ IDEA创建第一个Groovy工程
    Sublime Text3 安装ftp插件
    Sublime Text3配置Groovy运行环境
  • 原文地址:https://www.cnblogs.com/skyette/p/8408509.html
Copyright © 2011-2022 走看看