zoukankan      html  css  js  c++  java
  • 2021牛客OI赛前集训营-提高组(第二场)

    T1 串串串

    题目描述

    题面

    你有两个长度为 (n, m)(01)(S, T)

    (Q) 次询问,每次询问给出 (l_1, r_1, l_2, r_2),其中 (r_1 - l_1 + 1 = r_2 - l_2 + 1)(a = S[l_1 dots r_1]), (b = T[l_2 dots r_2]),你需要求出 (a eq b) 的位置个数对 (2) 取模。

    (n, m, q leq 2 imes 10^5)

    solution

    本题的点在对 (2) 取模上。

    (30) 分暴力就不说了。

    (100)

    算法一:

    可以发现,交换 (a) 中任意两个位置,答案是不变的,交换 (b) 中任意两个位置也一样。

    那么我们可以将 (a, b) 中得 (0) 放在前面,(1) 放在后面,答案即为 (a)(1) 的个数和 (b)(1) 的个数之差对 (2) 取模的结果。

    时间复杂度 (O(n + m + 1))

    std

    #include<iostream>
    #include<cstdio>
    #include<cassert>
    using namespace std;
    const int N=200005;
    int n,m,Q;
    char s[N],t[N];
    int a[N],b[N];
    int main()
    {
        scanf("%d%d",&n,&m);
        scanf("%s",s+1), scanf("%s",t+1);
        for(int i=1;i<=n;i++) a[i]=a[i-1]+(s[i]=='1');
        for(int i=1;i<=m;i++) b[i]=b[i-1]+(t[i]=='1');
        scanf("%d",&Q);
        while(Q--)
        {
            int l1,r1,l2,r2;
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            if(((a[r1]-a[l1-1])%2)==((b[r2]-b[l2-1])%2)) printf("0
    ");
            else printf("1
    ");
        }
        return 0;
    }
    
    

    算法二

    不难发现答案就是 (a)(b) 异或起来 (1) 的个数,如果为奇数就为 (1), 为偶数就为 (0)

    发现奇数个 (1) 异或起来正好等于 (1),偶数个异或起来等于 (0),所以直接两个区间的异或值异或起来就是答案。

    复杂度 (O(m + n))

    code

    /*
    work by:Ariel_
    Sorce:
    Knowledge:
    Time:
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #define ll long long
    #define rg register
    using namespace std;
    const int MAXN = 2e5 + 5;
    int read(){
        int x = 0,f = 1; char c = getchar();
        while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
        return x*f;
    }
    int preb[MAXN], prea[MAXN], n, m, a[MAXN], b[MAXN], Q;
    char s[MAXN], t[MAXN];
    int main(){
       n = read(), m = read();
       scanf("%s%s", s + 1, t + 1);
       for (int i = 1; i <= n; i++) a[i] = s[i] - '0', prea[i] = prea[i - 1] ^ a[i];
       for (int i = 1; i <= m; i++) b[i] = t[i] - '0', preb[i] = preb[i - 1] ^ b[i];
       Q = read();
       while(Q--) {
       	 int l = read(), r = read(), L = read(), R = read();
       	 int ret = prea[r] ^ prea[l - 1], res = preb[R] ^ preb[L - 1];
       	 int Ans = ret ^ res;
       	 cout<<Ans<<"
    ";
       }
       puts("");
       return 0;
    }
    

    T2 方格计数

    题目描述

    在左下角是 ((0,0)),右上角是 ((W,H)) 网格上,有 ((W+1) imes (H+1)) 个格点。

    现在要在格点上找 (N) 个不同的点,使得这些点在一条直线上。并且在这条直线上,相邻点之间的距离不小于 (D)。求方案数模 (10^9+7)

    题面

    (1 leq N leq 50, W, H, D leq 500, T leq 20)

    solution

    求方案数一开始想的是枚举直线,然后找出直线上的所有的点,然后 (dp) 求方案。但是现实是我不会枚举直线,暴力枚举直线上的点会炸掉,然后我就爆 (0) 了。

    正解

    知识点:组合数

    网格图上,一条直线上的两个点,(gcd(|x_1 - x_2|, |y_1, y_2|) - 1) 就是直线上两点间点的数目。

    证明

    任意两个元素间隔大于等于 (k) 的组合数,在 (A) 中选 (B) 个位置,要求每相邻位置要隔出至少 (C) 个空位置的方案。

    相邻的有 (B-1) 对,所以空 ((B-1)C) 个,去掉这些位置就变成普通的选位置了,答案就是 (C(A-(B-1)C,B))

    30pts

    虑枚举两个端点,强制两个端点选,令 (a) 为两个端点之间 (x) 轴上的距离,(b) 为两个端点 (y) 轴上的距离,那么这里面可以选择的点的个数有 (g=gcd(a,b)) 个。我们要求 (N-2) 个小球(强制两个端点选),需要放到 (g) 个盒子里,相邻两个小球的盒子编号差至少为 (k),方案数为

    [{N - 2 choose {g + 1 - 2k - (N - 3)(k - 1)}} ]

    复杂度 (O(TW^2H^2))

    100pts

    延续 (30) 分的思路,发现对于相同的 (a,b) 方案数也是相同的,考虑枚举 (a,b)(30) 分一样做,最后再乘个 ((W-a+1) imes (H-b+1)) 就好了,时间复杂度 (O(TWH))

    复杂度 (O(TWH))

    code

    /*
    work by:Ariel_
    Sorce:
    Knowledge:
    Time:
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    const int MAXN = 2000 + 5;
    const int mod = 1e9 + 7;
    int read() {
      int x = 0, f = 1; char c = getchar();
      while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
      while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
      return x * f;
    }
    int T, n, w, h, d, C[MAXN][MAXN];
    double dis(int a, int b, int x, int y) {
      return sqrt((y - b) * (y - b) + (x - a) * (x - a));
    }
    int gcd(int a, int b){return b == 0 ? a:gcd(b, a % b);}
    void Pre(){
       C[0][0] = 1;
       for (int i = 1; i <= 2000; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++) {
          C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
        }
       }
    } 
    long long work(int a, int b){
       if(a == 0 && b == 0) return 0;
       int g = gcd(a, b);
       int k = ceil(d / dis(0, 0, a / g, b / g));//相邻连的两点的编号差
       if(k * (n - 1) > g) return 0;
       int res = C[g + 1 - 2 * k - (k - 1) * (n - 3)][n - 2];
       if(a != 0 && b != 0) res = (res + res) % mod;
       res = (long long)res *(w - a + 1) * (h - b + 1) % mod;
       return res;
    }
    int main() {
       T =  read();
       Pre();
       while(T--) {
         n = read(), w = read(), h = read(), d = read();
         if(n == 1) {
           printf("%d
    ", (w + 1) * (h + 1));
           continue;
         }
         int Ans = 0;
         for (int i = 0; i <= w; i++) 
           for (int j = 0; j <= h; j++) Ans = (Ans + work(i, j)) % mod;
         printf("%d
    ", Ans);
       }  
       system("pause");
       return 0;
    }
    

    T3 树数树

    题目描述

    牛牛有一棵 (n) 个点的有根树,根为 (1)
    我们称一个长度为 (m) 的序列 (a) 是好的,当且仅当:

    • (forall i in (1, m], a_i)(a_{i - 1}) 的祖先或 (a_{i - 1})(a_i) 的祖先;

    • (forall 1 leq i < j leq m, a_i eq a_j)

    (n leq 10^5)

    题面

    solution

    错把祖先当作父亲,自裁 /kk

    算法一:

    (f_{u, i}) 表示 (u)(u) 的子树中,允许使用子树外 (i) 个祖先所得到的最长上升长度是多少,转移相当于各个儿子的一个(max) 卷积。

    时间复杂度:(O(n^2)) 期望得分 (30)

    算法二:

    可以发现,一个节点 (u) 可以将子树中的两个序列拼成一个序列,且我们在处理完 (u) 的父亲的时候 (u) 的状态已经不用管了,我们可以用堆维护出 (u)(u) 的子树中的点能组成的序列,转移相当于是将所有子树的堆合并成一个,然后取出其中最大的两个合并成一个。

    可以用启发式合并或者可并堆维护这个过程。

    时间复杂度 (O(nlog^2n) sim O(nlogn))

    下面是启发式合并的代码:

    /*
    work by:Ariel_
    Sorce:
    Knowledge:启发式合并
    Time:
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    using namespace std;
    const int MAXN = 2e5 + 5;
    
    int read() {
      int x = 0, f = 1; char c = getchar();
      while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
      while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
      return x * f;
    }
    int T, n, id[MAXN];
    struct edge{int nxt, v;}e[MAXN << 1];
    int E, head[MAXN];
    void add_edge(int u, int v) {
      e[++E] = (edge){head[u], v};
      head[u] = E;
    }
    void Clear() {
       memset(head, 0, sizeof head);
       E = 0;
    }
    priority_queue<int> q[MAXN];
    int Merge(int x, int y) {
       if(q[x].size() > q[y].size()) swap(x, y);
       while(!q[x].empty()) {
         int u = q[x].top(); q[x].pop();
         q[y].push(u);
       }
       return y;
    }
    void dfs(int x, int fa) {
      id[x] = x;
      while(!q[id[x]].empty())q[id[x]].pop();
      for(int i = head[x]; i; i = e[i].nxt) {
           int v = e[i].v;
           if(v == fa) continue;
           dfs(v, x);
           id[x] = Merge(id[x], id[v]);
      }
      if(q[id[x]].empty()) q[id[x]].push(1);
      else {
        int w = q[id[x]].top(); q[id[x]].pop();
        if(!q[id[x]].empty()) w += q[id[x]].top(), q[id[x]].pop();
        q[id[x]].push(w + 1);
      }
    }
    int main(){
       T = read();
       while(T--) {
         n = read();
         Clear();
         for (int i = 1; i < n; i++) {
            int u = read(), v = read();
            add_edge(u, v), add_edge(v, u);
         }
         dfs(1, 0);
         printf("%d
    ", q[id[1]].top());
       }
       system("pause");
       return 0;
    }
    

    T4 序列

    题面

    定义一个数的 se 序列为其一个数位和为 (10) 的子段。

    举个例子,1145141919810900 的所有 se 序列为

    • 145
    • 451
    • 514
    • 19
    • 91
    • 19
    • 109
    • 1090
    • 10900

    定义一个数是 ll 数,当且仅当它的每一个数位都在至少一个 se 序列中。

    举个例子,1145141919810900 不是 ll 数,因为第一个 1 和 8 不在任何一个 se 序列中,而 23541901 是一个 ll 数。

    现在牛牛想随机生成一个 ([0,10^n)) 范围内的数送给牛妹。具体地说,每一位上的数字为 (i) 的概率为 (a_i), 且保证 (sumlimits_{i=0}^9 a_i=1)
    现在牛牛想知道这个数为 ll 数的概率。

    数据范围

    对于 (5\%) 的数据,(n=1)

    对于 (5\%) 的数据,(n=100)

    对于 (20\%) 的数据,(n=3000)

    对于另 (30\%) 的数据,(nle 10^{18}),且保证 (forall iin [0,9],a_i=frac{1}{10})

    对于 (100\%) 的数据,(1le nle 10^{18}), (forall i in [0,9],0le b_ilt 10^9+7)

    solution

    正解:特征多项式。

    直接弃疗。

    std

    #include<iostream>
    #include<cstdio>
    #include<cassert>
    #include<cstring>
    #include<vector>
    #include<map>
    using namespace std;
    const int N=100005;
    const int MOD=1000000007;
    int ksm(int a,int b)
    {
        int res=1;
        while(b)
        {
            if(b&1) res=1LL*res*a%MOD;
            a=1LL*a*a%MOD,b>>=1;
        }
        return res;
    }
    int getinv(int x)
    {
        return ksm(x,MOD-2);
    }
    int n=2816*2;
    int cc[10];
    int a[N];
    vector<int>solve()
    {
        static vector<int>R[N];
        static int c;
        static int delta[N];
        static int fail[N];
        for(int i=1;i<=n;i++)
        {
            delta[c]=a[i];
            for(int j=0;j<(int)R[c].size();j++)
                delta[c]=(delta[c]-(long long)a[i-j-1]*R[c][j]%MOD+MOD)%MOD;
            if(delta[c]==0) continue;
            fail[c]=i;
            if(c==0)
            {
                R[++c]=vector<int>(i,0);
                continue;
            }
            int d=0;
            for(int j=1;j<c;j++)
                if((int)R[j].size()-fail[j]<(int)R[d].size()-fail[d]) d=j;
            int qwq=(long long)delta[c]*getinv(delta[d])%MOD;
            R[++c]=vector<int>(i-fail[d]-1,0);
            R[c].emplace_back(qwq);
            for(auto j:R[d])
                R[c].emplace_back((MOD-(long long)qwq*j%MOD)%MOD);
            if(R[c].size()<R[c-1].size()) R[c].resize(R[c-1].size());
            for(int j=0;j<(int)R[c-1].size();j++)
                R[c][j]=(R[c][j]+R[c-1][j])%MOD;
        }
        return R[c];
    }
    int k;
    long long val[N];
    int calc_f(long long n)
    {
        static int re[N];
        static int re_top;
        static int x[N];
        static int x_top;
        static int tp[N];
        static int tp_top;
        if(n<=k) return a[n];
        re[re_top=0]=1;
        x[x_top=1]=1;
        n-=k;
        for(;n;n>>=1)
        {
            if(n&1)
            {
                tp_top=re_top;
                for(int i=0;i<=re_top+x_top;i++)
                    tp[i]=re[i],re[i]=0;
                re_top=tp_top+x_top;
                for(int i=0;i<=tp_top;i++)
                    for(int j=0;j<=x_top;j++)
                        re[i+j]=(re[i+j]+(long long)tp[i]*x[j])%MOD;
                //times
                while(re_top>=k)
                {
                    for(int i=0;i<k;i++)
                        re[re_top-k+i]=(re[re_top-k+i]+(long long)val[k-i]*re[re_top])%MOD;
                    re[re_top]=0;
                    while(re[re_top]==0) re_top--;
                }
                //mod
            }
            tp_top=x_top;
            for(int i=0;i<=tp_top*2;i++)
                tp[i]=x[i],x[i]=0;
            x_top=tp_top+tp_top;
            for(int i=0;i<=tp_top;i++)
                for(int j=0;j<=tp_top;j++)
                    x[i+j]=(x[i+j]+(long long)tp[i]*tp[j])%MOD;
            //times
            while(x_top>=k)
            {
                for(int i=0;i<k;i++)
                    x[x_top-k+i]=(x[x_top-k+i]+(long long)val[k-i]*x[x_top])%MOD;
                x[x_top]=0;
                while(x[x_top]==0) x_top--;
            }
            //mod
        }
        int ans=0;
        for(int i=0;i<=re_top;i++)
            ans=(ans+(long long)re[i]*a[k+i])%MOD;
        return ans;
    }
    int tot;
    map<pair<long long,int>,int>book;
    void dfs(int les,long long val)
    {
        if(les==0)return;
        long long tp=val,q=0;
        book[{val,0}]=++tot;
        while(tp)
        {
            q++;
            book[{val,q}]=++tot;
            tp/=10;
        }
        for(int i=1;i<=min(les,9);i++)
            dfs(les-i,val*10+i);
        return;
    }
    vector<pair<int,int>>tr[2817];
    void solve(long long sta,int les)
    {
        int qwq=book[{sta,les}];
        tr[qwq].emplace_back(qwq,cc[0]);
        for(int i=1;i<=9;i++)
        {
            long long tp=sta,sum=0,id=-1;
            for(int j=0;j<=8;j++)
            {
                if(tp%10+sum+i>=10)
                {
                    id=j;
                    break;
                }
                sum+=tp%10,tp/=10;
            }
            if(id==-1)tr[qwq].emplace_back(book[{sta*10+i,les+1}],cc[i]);
            else
            {
                if(id+1<les) continue;
                long long nw=1;
                for(int j=1;j<=id;j++)
                    nw*=10;
                if(tp%10+sum+i==10) tr[qwq].emplace_back(book[{sta%nw*10+i,0}],cc[i]);
                else if(id>=les) tr[qwq].emplace_back(book[{sta%nw*10+i,les+1}],cc[i]);
            }
        }
        return;
    }
    int main()
    {
        long long m;
        assert(scanf("%lld",&m)==1);
        assert(m<=1e18);
        static int b[10];
        for(int i=0;i<=9;i++)
            assert(scanf("%d",&b[i])==1),assert(0<=b[i]&&b[i]<MOD);
        long long sum=0;
        for(int i=0;i<=9;i++)
            sum=(sum+b[i])%MOD;
        for(int i=0;i<=9;i++)
            cc[i]=(long long)b[i]*getinv(sum)%MOD;
        dfs(10,0);
        static int ans[2817];
        for(auto [vq,id]:book)
            if(vq.second==0) ans[id]=1;
        for(auto [vq,id]:book)
            solve(vq.first,vq.second);
        for(int i=1;i<=n;i++)
        {
            static int tt[2817];
            for(int j=1;j<=2816;j++)
                for(auto [v,c]:tr[j])
                    tt[j]=(tt[j]+(long long)ans[v]*c)%MOD;
            a[i]=tt[1];
            for(int j=1;j<=2816;j++)
                ans[j]=tt[j],tt[j]=0;
        }
        vector<int>ret=solve();
        k=ret.size();
        for(int i=1;i<=k;i++)
            val[i]=ret[i-1];
        int Zero=ksm(cc[0],m%(MOD-1));
        int res=(calc_f(m)-Zero+MOD)%MOD;
        printf("%d",res);
        return 0;
    }
    
  • 相关阅读:
    发送邮件时,报错:AttributeError: 'list' object has no attribute 'encode'
    快速统计字符出现次数
    vscode快捷键
    win7系统部署django项目
    记录一个小问题,django+Apache+win7,启动Apache后,打开网页,一直转圈圈,停不下来
    django ORM 按月分组统计
    哈希表的应用
    查找算法
    递归的应用
    递归
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15377231.html
Copyright © 2011-2022 走看看