zoukankan      html  css  js  c++  java
  • CCPC 2020 秦皇岛站 H题

    放个codeforces链接 点我

    主要参照YaoBIG的题解,这里记录一下题解中没提到的细节

    首先是套路的拆贡献,对于每一个$t$,答案为$left { a_i = k ight } + 2left { a_i = a_j = k (j < i) ight }$

    然后转化为枚举$i$和$(i, j)$统计每一个序列的贡献

    先看$left { a_i = k ight }$中是怎么做的,对于一个位置$i$如果填上$k$,很自然地可以想到分为$k$是首次出现和$k$不是首次出现两种情况。

    1、$k$如果是首次出现,那么$1- (i - 1)$的位置一定出现过了$1, 2, cdots, k - 1$这些数,$i$后面的数可以填$1, 2, 3, 4, cdots, k$以及$k + 1$,可以首先设$f(i, k)$表示长度为$i$的,已经最大值为$k$的方案数,然后考虑$i$后面的数怎么填 ,可以设$g(i, k)$表示从最大值为$k$的情况开始填,一直填$i$位的方案数,那么第一种情况的答案就是$f(i - 1, k - 1) * g(n - i, k)$。

    2、考虑$k$不是第一次出现,(我想不出来)那么$1 - (i  - 1)$的位置一定已经出现过了一个$j$满足$a_j$第一个$= k$,发现这样子的情况和1、中几乎完全一致,为 $f(j - 1, k - 1) * g(n - j - 1, k)$

    那么$left { a_i = t ight }$的答案就是$f(i - 1, k - 1) * g(n - i, k) + sum_{j = 1}^{i - 1}f(j - 1, k - 1) * g(n - j - 1, k)$,外层枚举$k, i$,做个前缀和

    然后考虑$left { a_i = a_j = k (j < i) ight }$怎么做,发现和$j$位置上的$k$是不是第一次出现有关,一个$(j, i)$的答案为$f(j - 1, k - 1) * g(n - j - 1, k) + sum_{t = 1}^{j - 1}f(t - 1, k - 1) * g(n - t - 2, k)$,$i$对这个答案没啥影响,一个$j$的答案即为一个$(j, i)$的答案直接乘上$(n - j)$

    现在考虑$f$和$g$怎么做,$f$的初值应该为$f(0, 0) = f(1, 1) = 1$,这里注意只有$f(0, 0)$代表啥都没有,是唯一一个$f(0, ?)$中的合法状态,如果有了$f(i, j)$,现在填第$i + 1$位,一种可行的填法是填$j + 1$,所以转移到$f(i + 1, j + 1)$,另外一种可行的填法是填$1 - j$中的任意一个数,所以也可以转移到$f(i + 1, j)$;而$g$的初值是$g(0, ?) = 1$,当我们从$g(i, ?)$转移到$g(i + 1, j)$时,我们考虑把之后填的数放到序列的后面,填上一个数当作这个序列的第一位,那么如果第一次填了$j$,是从$g(i, j - 1)$转移来的,如果不是第一次填$j$,则是从$g(i, j)$转移来的。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef double db;
    typedef pair <int, int> pin;
    
    const int N = 3005;
    
    int testCase, n;
    ll P, ans1[N], ans2[N], ans[N], f[N][N], g[N][N], sum1[N], sum2[N];
    
    namespace Fread {
        const int L = 1 << 15;
        
        char buffer[L], *S, *T;
        
        inline char Getchar() {
            if(S == T) {
                T = (S = buffer) + fread(buffer, 1, L, stdin);
                if(S == T) return EOF;
            }
            return *S++;
        }
        
        template <class T> 
        inline void read(T &X) {
            char ch; T op = 1;
            for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
                if(ch == '-') op = -1;
            for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
                X = (X << 1) + (X << 3) + ch - '0'; 
            X *= op;
        }
        
    } using namespace Fread;   
    
    namespace Fwrite {
        const int L = 1 << 15;
        
        char buf[L], *pp = buf;
        
        void Putchar(const char c) {
            if(pp - buf == L) fwrite(buf, 1, L, stdout), pp = buf;
            *pp++ = c;
        }
        
        template<typename T>
        void print(T x) {
            if(x < 0) {
                Putchar('-');
                x = -x;
            }
            if(x > 9) print(x / 10);
            Putchar(x % 10 + '0');
        }
        
        void fsh() {
            fwrite(buf, 1, pp - buf, stdout);
            pp = buf;
        }
        
        template <typename T>
        inline void write(T x, char ch = 0) {
            print(x);
            if (ch != 0) Putchar(ch);
            fsh();
        }
    
    } using namespace Fwrite;
    
    inline void inc(ll &x, ll y) {
        x += y;
        if (x >= P) x -= P;
    }
    
    int main() {
        #ifndef ONLINE_JUDGE
            freopen("sample.in", "r", stdin);
        #endif
    
        read(testCase);
        for (int _ = 1; _ <= testCase; ++_) {
            read(n), read(P);
            for (int i = 0; i <= n; i++) ans[i] = ans1[i] = ans2[i] = 0;
            for (int i = 0; i <= n; i++)
                for (int j = 0; j <= n; j++)
                    f[i][j] = g[i][j] = 0;
    
            if (P == 1) {
                printf("Case #%d:
    ", _);
                for (int i = 1; i <= n; i++)
                    printf("%lld%c", ans[i], " 
    "[i == n]);
                continue;
            }
            
            // for (int i = 0; i <= n; i++) g[0][i] = 1;
            f[0][0] = f[1][1] = 1;
            for (int i = 1; i < n; i++)
                for (int j = 1; j <= n; j++) {
                    if (j < n) inc(f[i + 1][j + 1], f[i][j]);
                    inc(f[i + 1][j], f[i][j] * j % P);
                }
    
            for (int i = 0; i <= n; i++) g[0][i] = 1;
            g[1][n] = n % P;
            for (int i = 1; i < n; i++) g[1][i] = (i + 1) % P;
            for (int i = 2; i <= n; i++)
                for (int j = 0; j <= n; j++) {
                    inc(g[i][j], g[i - 1][j] * j % P);
                    if (j < n) inc(g[i][j], g[i - 1][j + 1]);
                }
            
            // for (int i = 0; i <= n; i++) {
            //     for (int j = 0; j <= n; j++) printf("%lld ", f[i][j]);
            //     printf("
    ");
            // }
            // printf("
    ");
            // for (int i = 0; i <= n; i++) {
            //     for (int j = 0; j <= n; j++) printf("%lld ", g[i][j]);
            //     printf("
    ");
            // }
    
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    sum1[j] = sum1[j - 1];
                    if (n - j - 2 >= 0) inc(sum1[j], f[j - 1][i - 1] * g[n - j - 2][i] % P);
                    sum2[j] = sum2[j - 1];
                    if (n - j - 1 >= 0) inc(sum2[j], f[j - 1][i - 1] * g[n - j - 1][i] % P);
    
                    inc(ans1[i], 1LL * (n - j) * sum1[j - 1] % P);
                    if (n - j - 1 >= 0) inc(ans1[i], f[j - 1][i - 1] * g[n - j - 1][i] % P * (n - j) % P);
                    inc(ans2[i], sum2[j - 1]);
                    inc(ans2[i], f[j - 1][i - 1] * g[n - j][i] % P);
                }
    
                ans[i] = ans2[i];
                inc(ans[i], ans1[i]), inc(ans[i], ans1[i]);
            }
    
            // for (int i = 1; i <= n; i++)
            //     printf("%lld ", ans1[i]);
            // printf("
    ");
            // for (int i = 1; i <= n; i++)
            //     printf("%lld ", ans2[i]);
            // printf("
    ");
    
            // printf("Case #%d:
    ", _);
            // for (int i = 1; i <= n; i++)
            //     printf("%lld%c", ans[i], " 
    "[i == n]);
            printf("Case #"), write(_, ':'), puts("");
            for (int i = 1; i <= n; i++)
                write(ans[i], " 
    "[i == n]);
        }
        return 0;
    }
    View Code

    时间复杂度$O(n^2)$,要注意常数。

  • 相关阅读:
    Ubuntu(14.04LTS)学习札记
    【ABAP系列】SAP ABAP解析XML的示例程序
    【ABAP系列】SAP GUI740 PATCH5出现弹窗BUG
    【ABAP系列】SAP 获取工单和工序的状态
    【MM系列】SAP MM中物料帐下修改物料的价格
    【MM系列】SAP 物料帐下修改物料的价格
    【BASIS系列】SAP /usr/sap//DVEBMGS00满了怎么处理
    【MM系列】SAP MM物料账在制品承担差异功能及配置
    【SD系列】SAP 退货冲账过账成本更新
    【ABAP系列】SAP ABAP中使用for all entries in小结
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/13892671.html
Copyright © 2011-2022 走看看