• “蔚来杯“2022牛客暑期多校训练营9,签到题ABGIE


    题号 标题 已通过代码 通过率 团队的状态
    A Car Show 点击查看 1347/3168
    B Two Frogs 点击查看 777/2832
    C Global Positioning System 点击查看 41/136
    D Half Turns 点击查看 9/28
    E Longest Increasing Subsequence 点击查看 183/1423
    F Matrix and GCD 点击查看 79/369
    G Magic Spells 点击查看 461/3045
    H Radar Scanner 点击查看 0/79
    I The Great Wall II 点击查看 173/929
    J Colourful Journey 点击查看 2/25
    K NIO’s OAuth2 Server 点击查看 70/282

    A.Car Show

    链接:https://ac.nowcoder.com/acm/contest/33194/A
    来源:牛客网

    题目描述
    There are nn cities and mm car styles in NIO Kingdom. According to the investigations, for the ii-th city, cars of style T_iT
    i

    are the most popular.

    Now there will be a car show in NIO Kingdom, and the person in charge wants to choose an integer interval [l,r][l,r] (1\le l \le r \le n)(1≤l≤r≤n) and let the cities whose indices are in this interval hold the show jointly. And to manifest the variety of the cars, every style should occur at least once among the most popular styles in the host cities. Determine the number of integer intervals satisfying the constraint mentioned before.
    输入描述:
    The first line contains two integers nn and mm (1 \le m \le n \le 100,000)(1≤m≤n≤100000), denoting the number of cities and the number of car styles respectively.

    The second line contains nn integers T_1, T_2, \cdots, T_nT
    1

    ,T
    2

    ,⋯,T
    n

    (1 \le T_i \le m)(1≤T
    i

    ≤m), denoting the most popular styles.
    输出描述:
    Output one line containing one integer, denoting the answer.
    示例1
    输入
    复制
    5 3
    1 2 3 2 1
    输出
    复制
    5
    说明
    For the sample case, the 55 intervals are [1, 3], [1, 4], [1, 5], [2, 5], [3, 5][1,3],[1,4],[1,5],[2,5],[3,5] respectively.

    题意:

    • 给定一个长为𝑛的包含1,2,…,𝑚的序列,求有多少区间[𝐿,𝑅]包含所有1,2,…,𝑚
      𝑚≤𝑛≤10^5。

    思路:

    • 双指针,枚举左端点𝐿,合法的右端点集合一定是区间[𝑅,𝑛] ,且𝑅随着𝐿的递增而不减。
    • 在移动指针的同时维护区间内每种数字的个数以及出现数字的种类数 ,就可以在均摊𝑂(1)的时间对每个𝐿求出对应的𝑅。
    • 时间复杂度𝑂(𝑛)。
    #include
    using namespace std;
    typedef long long LL;
    const int maxn = 1e5+10;
    int a[maxn], b[maxn];
    int main(){
        ios::sync_with_stdio(0), cin.tie(0),cout.tie(0);
        int n, m;  cin>>n>>m;
        for(int i = 1; i <= n; i++)cin>>a[i];
        set<int>se;
        LL ans = 0;
        for(int l=1, r=0; l <= n; l++){
            while(r<=n && se.size()<m){
                r++;
                se.insert(a[r]);
                b[a[r]]++;
            }
            ans += n-r+1;  //对于当前的l, 从r开始包含所有1~m
            b[a[l]]--;     //l从当前区间去掉
            if(b[a[l]]==0)se.erase(a[l]);
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    B.Two Frogs

    链接:https://ac.nowcoder.com/acm/contest/33194/B
    来源:牛客网

    题目描述
    In the Lonely Mountain, there are a lot of treasure well protected by dwarfs. Later, one day, the last dragon Smaug came and sensed the treasure being. As known to all, dragons are always much too greedy for treasure. So definitely, the war between dwarfs and Smaug begins.

    During the war, two goblins Alice and Bob are turned into frogs by Gandalf, The Grey. In front of them, there are nn lotus leaves in a line. In order to break the spell, they must jump from the 11-st lotus leaf to the nn-th lotus leaf. If the frog is now on the ii-th lotus leaf instead of the nn-th lotus leaf, it can jump to a lotus leaf in range (i, i + a_i](i,i+a
    i

    ].

    Goblins are lack of intelligence and it’s also true even after turned into frogs. So Alice and Bob will jump randomly, which means, they will separately pick an available lotus leaf in every jump uniformly at random.

    Since Alice and Bob have already being playing games for decades, so they want to know the probability that they jump to the nn-th lotus leaf with the same count of jumps.
    输入描述:
    The first line contains an integer nn (2 \leq n \leq 8,000)(2≤n≤8000), denoting the number of lotus leaf.

    The second line contains n-1n−1 integers a_1, a_2, \ldots, a_{n-1}a
    1

    ,a
    2

    ,…,a
    n−1

    , where the ii-th integer a_ia
    i

    (1 \leq a_i \leq n-i)(1≤a
    i

    ≤n−i) indicates the range of lotus leaves that can be reached from the ii-th lotus leaf.
    输出描述:
    Output a line containing a single integer, indicating the probability that Alive and Bob jump to nn-th lotus leaf with the same count of jumps, taken modulo 998,244,353998244353.

    Formally speaking, let the result, which is a rational number, be \frac{x}{y}
    y
    x

    as an irreducible fraction, you need to output x \cdot y^{-1} \bmod{998,244,353}x⋅y
    −1
    mod998244353, where y^{-1}y
    −1
    is a number such that y \cdot y^{-1} \equiv 1 \pmod{998,244,353}y⋅y
    −1
    ≡1(mod998244353). You may safely assume that such y^{-1}y
    −1
    always exists.
    示例1
    输入
    复制
    5
    1 1 1 1
    输出
    复制
    1
    示例2
    输入
    复制
    5
    4 3 2 1
    输出
    复制
    440198031

    题意:

    • 河道里有𝑛个荷叶排成一排,从第𝑖(<𝑛)个荷叶出发可以跳到第(𝑖,𝑖+𝑎_𝑖]个荷叶上。
    • 有两只青蛙从第1个荷叶出发,每一步都独立地等概率随机地跳向后边的荷叶,求两只青蛙以相同步数到达第𝑛个荷叶的概率。
    • 𝑛≤8000,保证1≤𝑎_𝑖≤𝑛−𝑖。

    思路:

    • 开始题意理解错了,去算了用i步到第j个点的方案数,所以样例怎么都算不出来。
      用了到达第n个点的,不同步数对应的方案数平方后加起来,所以第二个样例怎么算都是总共只有8种方案。
      但是题目事实上从第一个点跳到第二个点时的概率已经乘上了1/4,再从2号点出发往任何点跳都是带上了前面的系数和权重的,所以到最后的概率就不能简单的用方案数去乘。所以需要dp。
    • 𝑓(𝑠,𝑖)表示从第1个荷叶出发恰好𝑠次跳到第𝑖个荷叶的概率
      考虑向后转移,𝑓(𝑠,𝑖)对 𝑓(𝑠+1,𝑗) (𝑖<𝑗≤𝑖+𝑎𝑖) 有𝑓(𝑠,𝑖) ∕𝑎𝑖 的贡献,前缀和优化即可。
    • 复杂度O(n^2)
    //时间优化
    #include
    using namespace std;
    typedef long long LL;
    const LL maxn = 8010, mod = 998244353;
    void exgcd(LL a, LL b, LL &d, LL &x,  LL & y, LL MOD) { if (b==0) { d = a; x = 1; y = 0; } else { exgcd(b, a % b, d, y, x, MOD); y -= x * (a / b); } }
    LL inv(LL a, LL MOD) { LL d=0, x=0, y=0; exgcd(a, MOD,  d,  x, y, MOD); return d == 1 ? (x + MOD) % MOD : -1; }
    
    LL a[maxn], b[maxn], dp[maxn][maxn]; //到达位置i,走了j步的概率
    
    int main(){
        ios::sync_with_stdio(0), cin.tie(0),cout.tie(0);
        LL n;  cin>>n;
        for(int i = 1; i < n; i++)cin>>a[i], b[i]=inv(a[i],mod);
        dp[1][0] = 1;  dp[2][0] = -1;
        for(int i = 1; i <= n; i++){
            for(int k = 0; k < i; k++){
                //前缀和更新
                dp[i][k]=(dp[i][k]+dp[i-1][k])%mod;
                //转移:dp[i+1,i+a[i]][k+1] += dp[i][k]/a[i]
                dp[i+1][k+1]=(dp[i+1][k+1]+dp[i][k]*b[i])%mod;
                dp[i+a[i]+1][k+1]=(dp[i+a[i]+1][k+1]-dp[i][k]*b[i])%mod;
                // for(int j=i+1;j<=i+a[i];j++){
                //     dp[j][k+1]=(dp[j][k+1]+dp[i][k]*inv(a[i],mod)+mod)%mod;
                // }
            }
        }
        LL ans = 0;
        for(int i = 1; i < n; i++)ans = (ans+dp[n][i]*dp[n][i]%mod)%mod;
        cout<<ans<<"\n";
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    //空间优化
    #include
    using namespace std;
    typedef long long LL;
    const int maxn = 8010, mod = 998244353;
    void exgcd(LL a, LL b, LL &d, LL &x,  LL & y, LL MOD) { if (b==0) { d = a; x = 1; y = 0; } else { exgcd(b, a % b, d, y, x, MOD); y -= x * (a / b); } }
    LL inv(LL a, LL MOD) { LL d=0, x=0, y=0; exgcd(a, MOD,  d,  x, y, MOD); return d == 1 ? (x + MOD) % MOD : -1; }
    
    LL a[maxn], b[maxn], dp[maxn];
    
    int main(){
        ios::sync_with_stdio(0), cin.tie(0),cout.tie(0);
        LL n;  cin>>n;
        for(int i = 1; i < n; i++)cin>>a[i], b[i] = inv(a[i],mod), a[i]=min(n,i+a[i])+1;
        LL ans = 0;
        dp[1] = 1;
        for(int i = 0; i <= n; i++){             //dp[j]:跳i次跳到第j个荷叶的概率
            ans = (ans+dp[n]*dp[n]%mod)%mod;
            dp[n] = 0;
            for(int j = n-1; j >= 0; j--){       //通过荷叶j来跳第i次,概率*1/a[j]
                LL t = dp[j]*b[j]%mod;           //概率+=dp[i-1][j]*1/a[j]
                dp[j+1] = (dp[j+1]+t+mod)%mod;   //给[j+1,j+a[j]] += t
                dp[a[j]] = (dp[a[j]]-t+mod)%mod; 
                dp[j] = 0;
            }
            for(int j = 1; j <= n; j++){          //前缀和更新
                dp[j] = (dp[j]+dp[j-1])%mod;
            }
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    G.Magic Spells

    链接:https://ac.nowcoder.com/acm/contest/33194/G
    来源:牛客网

    题目描述
    One day in the magic world, the young wizard RoundDog was learning the compatibility of spells. After experimenting for a long time, he reached the conclusion that the compatibility of a spell collection can be measured by the number of distinct palindromes that are substrings of all the spells in the collection. He was so excited and planned to write a program to calculate the compatibility of any input spell collection.

    However, RoundDog was busy participating the NowWizard Multi-Universe Training this week. He was too struggling during the competition and feels tired now.

    Since RoundDog is not in the mood to finish the program now, could you help him?
    输入描述:
    The first line contains a single integer kk (1 \leq k \leq 5)(1≤k≤5), indicating the number of spells in a spell collection.

    In the following kk lines, each line contains a spell SS (1 \le |S| \le 300,000)(1≤∣S∣≤300000), which is a string containing only lowercase English letters.

    It is guaranteed that the total length of the spells does not exceed 300,000300000.
    输出描述:
    Output the compatibility of the input spell collection, which is the number of distinct palindromes that are substrings of all the spells in the collection.
    示例1
    输入
    复制
    3
    abaca
    abccaca
    acabb
    输出
    复制
    4
    说明
    In the example, “a”, “b”, “c”, “aca” are the four distinct palindromes that are substrings of all the input spells.

    题意:

    • 给定𝑘个字符串𝑆_1,𝑆_2,…,𝑆_𝑘,求有多少个本质不同的公共回文子串
    • 𝑘≤5,|𝑆_𝑖|≤3×10^5

    思路:

    • 因为k<=5,所以可以先对每个字符串𝑆分别求出所有本质不同回文子串,总共有𝑂(|𝑆|)个。
      可以使用Manacher算法配合Hash求解,也可以使用回文树求解。
    • Hash统计每个字符串的出现次数即可,时间复杂度𝑂(|𝑆|log⁡|𝑆|)或𝑂(|𝑆|(log⁡|S|+𝛴))。
    • 也可以直接回文自动机求解。
    //manacher+hash
    #include
    using namespace std;
    typedef long long LL;
    const int maxn = 6e5+10;
    
    char s[maxn];
    map<pair<LL,LL>,int>mp; //字符串[l,r]的出现次数
    
    //字符串双值hash(降低冲突)
    const LL mod = 1000000007, mod2 = 998244353, base = 146;
    char ch[maxn];
    LL Sum1[maxn], Pow1[maxn], Sum2[maxn], Pow2[maxn], N;
    void init(){
        Pow1[0] = Pow2[0] = 1;
        for (int i = 1; i <= 300000; i ++){
            Pow1[i] = Pow1[i-1] * base % mod;
            Pow2[i] = Pow2[i-1] * base % mod2;
        }
    }
    void init_str(char *s){
        int n = strlen(s + 1);
        ch[0] = '@';  ch[n*2+1] = '#';  ch[n*2+2] = '\0';
        Sum1[0] = Sum2[0] =  0;
        for (int i = 1; i <=n; i++){
            Sum1[i] = (Sum1[i-1] * base % mod + s[i]) % mod;
            Sum2[i] = (Sum2[i-1] * base % mod2 + s[i]) % mod2;
            ch[i*2] = s[i];
            ch[i*2-1] = '#';
        }
        N = 2*n+1;
    }
    inline pair<LL,LL> get_hash(int l, int r){
        LL hash1 = (Sum1[r] - Sum1[l-1] * Pow1[r - l + 1] % mod + mod) % mod;
        LL hash2 = (Sum2[r] - Sum2[l-1] * Pow2[r - l + 1] % mod2 + mod2) % mod2;
        return {hash1, hash2};
    }
    
    //马拉车,求s的本质不同的回文子串
    int r[maxn];
    void manacher(int id){
        r[1] = 1;                                          //r[i]:以i为中心的最长回文子串
        int k = 1;
        for(int i = 2; i <= N; i++){
            int p = k+r[k]-1;
            if(i <= p)r[i] = min(r[2 * k - i], p - i + 1); //没有超过边界,用对称性求解
            else r[i] = 1;                                 //至少为1
            while(ch[i+r[i]] == ch[i-r[i]])r[i]++;         //超过边界时,暴力匹配
            //枚举以i为中心的回文串, k维护已经枚举过的边界
            if (i + r[i] > k + r[k]){                      
                for (int R = k+r[k]+1; R <=i+r[i]; R++){
                    int L = 2*i-R;
                    //判断回文串[L,R], 如果次数<当前id,就去掉
                    if(ch[R]=='#'){
                        pair<LL,LL>hashvalue = get_hash(L/2+1,R/2);
                        if(id>1 && !mp.count(hashvalue))continue;
                        if(id>1 && mp[hashvalue]<id-1){
                            mp.erase(hashvalue);  continue;
                        }
                        if(mp[hashvalue] == id)continue;
                        mp[hashvalue] = id;
                    }
                }
                k = i;
            }
        }
    }
    
    int main(){
        init();
        int n;  scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%s", s+1);
            init_str(s);
            manacher(i);
        }
        int ans = 0;
        for(auto x : mp){
            if(x.second==n)ans++;
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    //回文自动机
    #include
    using namespace std;
    const int maxn = 6e5+10;
    
    //Palindromic Tree, 仅接受某个字符串的所有回文子串的中心及右半部分, O(n)
    struct PAM{
        int ch[maxn][27],s[maxn],fail[maxn],len[maxn],num[maxn],cnt[maxn];
        int n,tot,last;
        int newnode(int x){len[tot]=x;return tot++;}//建立一个新节点,长度为x
        void init(){                                //初始化
            newnode(0);newnode(-1);                 //状态0和-1,分别作为偶回文子串和奇回文子串两棵树的根
            fail[0]=1;fail[1]=1;
            n=last=0;s[n]=-1;
        }
        //找后缀回文
        //fail边,表示真后缀中在自动机里的最长状态(也就是最长回文真后缀)
        int getfail(int x){while(s[n]!=s[n-len[x]-1])x=fail[x];return x;}
        //建树
        //性质:在一个字符串后添加一个字符,至多增加一个之前没有出现过的回文子串,且该回文子串必定是原串的一个回文真后缀两侧加上新添加的这个字符
        void add(int x,int pos,int id){
            s[++n]=x;                      //当前添加的字符为x
            int t=getfail(last);           //找上次到达的状态, 往后添加一个字符
            if(!ch[t][x]){                 //如果后一个字符的状态不存在,就新建
                int now=newnode(len[t]+2); //转移表示在串的两侧各加上同一个字符,因此len+2
                fail[now]=ch[getfail(fail[t])][x];
                ch[t][x]=now;
                num[now]=num[fail[now]]+1;
            }
            last=ch[t][x];                 //跳转到当前状态
            cnt[last]|=(1<<id);            //状压,记录在第id个里存在该回文串
        }
        void build(char *s,int id){
            int N=strlen(s);
            for(int i=0;i<N;++i)add(s[i]-'a'+1,i,id);
        }
    }pam;
    
    char s[maxn];
    
    int main(){
        pam.init();
        int n;  scanf("%d",&n);
        for(int i = 0; i < n; i++){
            scanf("%s",s);
            pam.last = 0;  pam.n = 0;         //对每个串重新跑回文自动机(维护cnt不删)
            pam.build(s,i);
        }
        int ans = 0;
        for(int i=0;i<pam.tot;++i){           //遍历回文树所有节点
            ans += (pam.cnt[i]==((1<<n)-1));  //如果某个串在n次里都出现了,ans+1
        }
        cout<<ans<<"\n";
        return 0;
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    E.Longest Increasing Subsequence

    链接:https://ac.nowcoder.com/acm/contest/33194/E
    来源:牛客网

    题目描述
    Given an integer mm, you need to construct a permutation pp whose length does not exceed 100100 such that there are exactly mm longest increasing subsequences in pp.

    If multiple solutions exist, print any of them. If no solution exists, report it.
    输入描述:
    The first line contains an integer TT (1\le T \le 1,000)(1≤T≤1000), denoting the number of test cases.
    Each test case contains an integer mm (1\le m \le 10^9)(1≤m≤10
    9
    ) in one line.
    输出描述:
    For each test case:
    If no solution exists, print “-1” (without quotes) in one line.
    Otherwise, print one integer nn (1\le n\le 100)(1≤n≤100) in the first line denoting the length of the permutation you construct. Then print nn integers p_1, p_2, \cdots, p_np
    1

    ,p
    2

    ,⋯,p
    n

    , (1 \le p_i \le n, \forall , 1 \le i < j \le n, p_i \neq p_j)(1≤p
    i

    ≤n,∀1≤i i



    =p
    j

    ) in the second line denoting the permutation you construct.
    示例1
    输入
    复制
    2
    3
    5
    输出
    复制
    4
    2 4 1 3
    5
    3 2 5 1 4
    说明
    In the first test case, the length of the LIS (longest increasing subsequence) is 22, and the 33 LISs are {2, 4}, {2, 3}, {1, 3}{2,4},{2,3},{1,3}.
    In the second test case, the length of the LIS is 22, and the 55 LISs are {3, 5}, {3, 4}, {2, 5}, {2, 4}, {1, 4}{3,5},{3,4},{2,5},{2,4},{1,4}.

    题意:

    • 构造一个1,2,…,𝑛的排列,使其恰好有𝑚个不同的最长上升子序列。
    • 𝑚≤10^9,要求𝑛≤100。题目保证有解。

    思路:

    • 注意题目是,最长上升子序列,不是上升子序列,如果存在长为3的子序列,那么长为2的就不能统计为答案。
    • 记𝑚的二进制表示为𝑏𝑘, 𝑏(𝑘−1)…𝑏0。
      先构造2,1,4,3,6,5,…,2𝑘,2𝑘−1,此时原序列的LIS长度为k(方案数为2^k, 2个连续的数必须选1个)。
    • 然后低到高依次考虑𝑚的二进制位。
      如果𝑏𝑖=1,就在第2𝑖个数之后插入一个大于2𝑘的数字𝑝𝑖。可以发现,对于每个pi插入后,pi前方的2i个数都小于它,且连续的2个数必须且最多选1个,因此多了2^i个长为i+1的LIS。
    • 我们适当取值使得这些𝑝𝑖是递增的,因为有k位,理想情况(全为1时)插入了k次,全部插入后,后面的数与当前的pi一起构成LIS,相当于此时原序列的LIS长度为k+1。同时因为后面的pi数取值是唯一的,相当于LIS的个数多了2^i个(前面每个数选或不选)。
    • 我们再考虑不理想的状况,即如果是bi=0的情况。此时不能在对应的2i个数后面插入值,不然会导致LIS个数增加,但是如果不插入又会导致原序列的LIS长度不够。我们可以在包含𝑝𝑖 (𝑖<𝑘)的pi后面插入一些递增的数字,可以发现此时不会让之前的 2^i 个数变多,因为选择是唯一的。最后我们只要调整所有的pi保证递增即可。
      同时,可以发现𝑝𝑖 (𝑖<𝑘)后边需要插入的数字个数就是𝑚的二进制表示下第𝑖位往上连续0的个数。
    #include
    using namespace std;
    int main(){
        ios::sync_with_stdio(0), cin.tie(0),cout.tie(0);
        int T;  cin>>T;
        while(T--){
            int m;  cin>>m;
            int len = 32-__builtin_clz(m);
            vector<int>cnt(len);                //2i位后需要插入的数字个数
            for(int i = 0,lt=-1; i < len; i++){ //lt维护每个1后面连续的0的个数
                if(m>>i&1)cnt[lt=i]++;          //对于这位为1至少要插入一个
                else if(lt>=0)cnt[lt]++;        //0则是插在上个1的后面
            }
            vector<int>res;
            int low=0, high=2*len-2;
            for(int i = 0; i < len; i++){
                if(i>0)res.push_back(low+2),res.push_back(low+1), low+=2;
                for(int j = 0; j < cnt[i]; j++)res.push_back(++high);
            }
            cout<<res.size()<<"\n";
            for(int i = 0; i < res.size(); i++){
                cout<<res[i]<<" \n"[i+1==res.size()];
            }
        }
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    I.The Great Wall II

    链接:https://ac.nowcoder.com/acm/contest/33194/I
    来源:牛客网

    题目描述
    Beacon towers are built throughout and alongside the Great Wall. There was once a time when there were nn beacon towers built from west to east for defending against the invaders. The altitude of the ii-th beacon tower, based on historical records, is a_ia
    i

    .

    The defenders divide strategically all beacon towers into kk parts where each part contains several, but at least one, consecutive beacon towers. To fully defend against the invaders, to each part a team of defenders should be assigned, the cost of which is given by the highest altitudes of beacon towers in that part.

    As a historian, you are dying to know the minimum costs of assignments for every k = 1, 2, \ldots, nk=1,2,…,n.
    输入描述:
    The first line contains an integer nn (1 \leq n \leq 8,000)(1≤n≤8000), denoting the number of beacon towers alongside the Great Wall.

    The second line contains nn integers a_1, a_2, \ldots, a_na
    1

    ,a
    2

    ,…,a
    n

    , where the ii-th integer a_ia
    i

    (1 \leq a_i \leq 100,000)(1≤a
    i

    ≤100000) is the altitude of the ii-th beacon tower.
    输出描述:
    Output nn lines, the ii-th of which contains an integer indicating the minimum cost for k = ik=i.
    示例1
    输入
    复制
    5
    1 2 3 4 5
    输出
    复制
    5
    6
    8
    11
    15
    示例2
    输入
    复制
    5
    1 2 1 2 1
    输出
    复制
    2
    3
    4
    6
    7

    题意:

    • 给定长为𝑛的整数序列,将其分为非空的𝑘段使得每一段的最大值之和最小,对𝑘=1,2,…,𝑛分别求解。
    • 𝑛≤8000。

    思路:

    • 𝑑𝑝(𝑖,𝑗)表示将𝑎1,𝑎2,…,𝑎𝑗分为𝑖段的最小代价。
      转移时已经知道分为i-1段的最小代价,枚举k 𝑑𝑝(𝑖,𝑗)=min(1≤𝑘≤𝑗)⁡{ 𝑑𝑝(𝑖−1,𝑘−1)+max⁡{𝑎𝑘,𝑎(𝑘+1),…,𝑎_𝑗} },直接做的复杂度是𝑂(𝑛^3)。

    • 对于固定的𝑗,当𝑘从𝑗开始遍历到1时,max⁡{𝑎𝑘,𝑎(𝑘+1),…,𝑎j}是不减的,每一个取值对应一段𝑘。
      新增一个𝑎(𝑗+1)时末尾一些段会被合并成一个max值是𝑎(𝑗+1)的段,可以使用单调栈维护所有段。
      每个数最多入栈和出栈一次,因此遍历一次整个数组时维护单调栈的复杂度是𝑂(𝑛)。

    • 假设当前的段是[𝑙1,𝑟1],[𝑙2,𝑟2],…,[𝑙𝑠,𝑟𝑠],对应的max值是𝑣1,𝑣2,…,𝑣𝑠。
      转移式改写为𝑑p(𝑖,𝑗)=min(1≤𝑡≤𝑠)⁡{min⁡{𝑑𝑝(𝑖−1,𝑙𝑡 ),𝑑𝑝(𝑖−1,𝑙𝑡+1),…,𝑑𝑝(𝑖−1,𝑟_𝑡 )}+𝑣𝑡}。

    #include
    using namespace std;
    typedef long long LL;
    typedef pair<LL,LL> PLL;
    const int maxn = 8010;
    LL a[maxn], dp[maxn][maxn];  //将前i个数分为j段的最小代价(每一段的最大值之和)
    
    int main(){
        int n;  cin>>n;
        for(int i = 1; i <= n; i++)cin>>a[i];
        memset(dp, 0x3f, sizeof dp);
        dp[0][0] = 0;   dp[1][1] = a[1];
        for(int i = 2; i <= n; i++)dp[i][1] = max(dp[i-1][1], a[i]);
        for(int j = 2; j <= n; j++){       //枚举划分为j段的最小代价
            vector<PLL>stk;                //维护每一段最大值的位置和对应的最小代价,单调递减
            stk.push_back({0,dp[0][j-1]});
            for(int i = 1; i <= n; i++){   //考虑把第i个数放到前面的某一段里去
                LL mi = dp[i-1][j-1];      //第i个数单独成段的最小代价
                while(stk.back().first!=0 && a[stk.back().first]<a[i]){
                    mi = min(mi,stk.back().second);  //最大值
                    stk.pop_back();
                }
                //a[i]可以放到最大值>当前的段里,不需要代价。 或者单独成段,代价+a[i]。
                dp[i][j] = min(dp[stk.back().first][j], mi+a[i]);
                stk.push_back({i,min(mi, dp[i][j-1])});
            }
        }
        for(int i = 1; i <= n; i++)cout<<dp[n][i]<<"\n";
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    前端性能精进(七)——构建
    接口测试面试秘籍,一套搞定接口测试
    不同类型的软件企业该如何有效的管理好你的软件测试团队?
    SQL Server教程 - T-SQL-存储过程(PROCEDURE)
    Pandas数据分析及可视化应用实践
    推特被封号怎么办?如何防封?
    【个人博客搭建】(24)统一api接口返回格式
    Python可变参数*args和**kwargs
    koa - 洋葱模型浅析
    计算机毕业设计选什么题目好?springboot 学生考勤管理系统
  • 原文地址:https://blog.csdn.net/qq_33957603/article/details/126351199