CF1819D Misha and Apples 题解

· · 题解

显然,我们要让最后一次清空最提前。

那么我们设 f_iS_i 之前最后一次被清空最提前的位置。

然后我们要满足以下几个条件:

前面那个条件很容易满足,预处理一下不能被清空的最后一位在哪里就行了。(记作 mx_i

后面那个条件,我们可以再设一个 g_i,为到 i 能否清空。

那么 f_i 的转移就可以是:

然后我们可以先更新 g_i,再更新 f_{i+1}

最后,统计答案的时候,如果 (f_n, n] 中有空集,那么直接输出 m,否则只要模拟一下即可。

由于 \sum m 没有保证,所以需要注意一下清空的复杂度。

代码:

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;

int t, k[N], n, m, lst[N], mx[N], g[N], lstcan[N];
vector<int> s[N];
queue<int> q;

int main(){
    scanf( "%d", &t );
    while( t-- ){
        scanf( "%d%d", &n, &m );
        for( int i = 1; i <= n; ++i ){
            mx[i] = 0;
            scanf( "%d", &k[i] );
            s[i].resize( k[i] + 1 );
            if( k[i] == 0 ) lstcan[i] = i; else lstcan[i] = lstcan[i - 1];
            for( int j = 1; j <= k[i]; ++j ){ scanf( "%d", &s[i][j] ); mx[i] = max( lst[s[i][j]], mx[i] ); lst[s[i][j]] = i; q.push( s[i][j] ); }
        }
        for( int i = 1; i <= n + 1; ++i ) g[i] = 0;
        g[0] = 1;int f = 0; //其实 f 没必要开成数组,现场计算现场用即可。
        for( int i = 1; i <= n; ++i ){
            if( lstcan[i] > f || mx[i] > f ) g[i] = 1; else g[i] = 0;
            while( g[f] == 0 || f < mx[i] ) ++f;
        }
        int ans = 0;
        if( lstcan[n] > f ) printf("%d\n", m);
        else{ for( int i = f + 1; i <= n; ++i ) ans += k[i]; printf("%d\n", ans); }
        while( !q.empty() ) lst[q.front()] = 0, q.pop(); //注意清空的时间复杂度
    }
    return 0;
}