1 solutions

  • 2
    @ 2023-12-23 8:33:12

    神奇消消乐。。emm,确实挺神奇的。

    题意

    题意很简单:给定循环次数 NN,每次向数列末尾添加一个数 aia_i。如果数列的某个子段满足消除的条件,就会删除这个子段。对于每次操作,询问数列的长度。

    消除条件:连续的 kk 个数,每个数都是 kk

    思路

    首先要明确一点:每次操作时,仅会在数列末尾产生至多一次删除操作。

    推导:假定当前数列中没有可删除的子段,即,最多只有连续 k1k-1kk。而每次添加的数有可能刚好凑齐了连续 kkkk,并发生了删除操作。

    我们发现,所有删除操作都在数列末尾,这与栈 stackstack 的弹出操作很像啊!考虑用栈维护。

    考虑维护两个栈。一个栈 stk 存放数列中的数 aia_i,以便和新添加的数做比较。另一个栈 cnt 存放数列中 对应位置上 aia_i 的连续长度 bib_ibi[1,ai)b_i\in[1,a_i)。(也就是说当前有 bib_i 个连续的 aia_i

    如果发现 bi=aib_i=a_i,在两个栈中同时弹出 bib_i 个元素,以此模拟消除操作。

    实现

    维护两个栈,两个栈还要同步操作,而且还要 O(1)O(1) 弹出多个元素。用STL显然力不从心了,考虑手动维护两个栈,这样每次进出栈的复杂度就都是 O(1)O(1) 了。每次操作输出栈的大小即可。

    #include <iostream>
    using namespace std;
    const int N = 2e5+10;
    int n, stk[N], cnt[N], tot;  // tot:栈的大小,默认为0
    int main() {
        cin>>n;
        stk[0] = -1;  // 记得考虑设置初始值(本题没有必要)
        for(int i=1;i<=n;++i) {
            // 加入新元素
            cin>>stk[++tot];
            cnt[tot] = 1;
            if(stk[tot]==stk[tot-1])  // 如果相等,更新长度cnt
                cnt[tot] += cnt[tot-1];
            // 如果满足删除条件,按要求删除栈顶的stk[tot]个元素
            if(cnt[tot]==stk[tot])
                tot-=stk[tot];
            cout<<tot<<"\n";  // 输出栈的大小
        }
        return 0;
    }
    
    • @ 2023-12-23 11:22:44

      我的解法:只用一个栈,栈内压的元素是

      struct node {
          int num; // 当前结点保存的数字
          int cnt; // 当前数字连续出现的次数
      }
      

      也就是把连续多个数压成一个结点保存,这样就会避免频繁的进出栈,每次进栈出栈只需要修改 cnt 即可。

      tot 变量(数字的总数)非常重要!!!每次进出栈只需要修改该值即可。如果每次输出数字个数都需要遍历栈中所有元素,那么当入栈数字都不相同时,时间复杂度将会高达 O(n2)O(n^2)

    • @ 2023-12-26 13:51:11

      就用老师的struct立!@

  • 1

Information

ID
937
Time
1000ms
Memory
256MiB
Difficulty
9
Tags
(None)
# Submissions
60
Accepted
6
Uploaded By