• 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. 导一导

  • 相关阅读:
    NOIP普及组2003经验总结
    Day6上午 DP练习题
    Day4 图论
    Day3 数据结构
    使用ettercap进行dns欺骗和获取目标浏览的图片
    flask入门
    攻防世界-web-unserialize3
    数据结构课设作业-----飞机订票系统
    bugku NaNNaNNaNNaN-Batman
    it's a test
  • 原文地址:https://www.cnblogs.com/skyette/p/8408509.html
Copyright © 2020-2023  润新知