1 条题解

  • 0
    @ 2025-8-24 23:03:28

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar sdyzpf
    不卖了

    搬运于2025-08-24 23:03:28,当前版本为作者最后更新于2024-08-29 15:28:43,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    思路

    出题人题解。首先思考一个结论,最优的粉碎方案一定是粉碎整个序列的一段前缀,欲证明该结论,只需考虑最后一次粉碎操作即可。如果一对能匹配上的牌分别出现在原序列的 llrr 两个位置,则我们可以通过操作把 [1,r][1,r] 的牌全粉碎掉。所以,计算最多能粉碎多少张牌,可以转化成求出最后一张可以匹配的牌的位置。

    dpidp_i 表示把 [1,i1][1,i-1] 中所有和 aia_i 相等的牌全部粉碎掉的可行性,其中 dpi=0dp_i=0 表示不可行, dpi=1dp_i=1 表示可行。令 preipre_i 表示上一张和 aia_i 相等的牌的位置。注意到,作为更晚出现的牌,ii 只有可能和 preipre_i 进行匹配,所以 ii 是一对可以匹配的牌中更晚被插入的那张,当且仅当 dpprei=1dp_{pre_i}=1

    考虑如何进行转移。dpi=[prei能否在i之前被粉碎]dp_i=[pre_i能否在i之前被粉碎]preipre_i 被粉碎有两种方式,一种是与 prepreipre_{pre_i} 匹配,另一种是被 j(prei,i)j\in(pre_i,i)jjprejpre_j 匹配粉碎掉了。两种方式分别对应以下两种转移:

    dpi=dpprepreidp_i|=dp_{pre_{pre_i}} dpi=dpprej,j(prei,i)dp_i|=dp_{pre_j},j\in(pre_i,i)

    合并一下就是:

    dpi=dpprej,j[prei,i)dp_i|=dp_{pre_j},j\in[pre_i,i)

    此时我们已经得到的 O(n2)O(n^2) 的做法,优化也是容易的,前缀和优化即可,转移如下:

    ci=ci1+fpreic_i=c_{i-1}+f_{pre_i} dpi=[cprei1<ci1]dp_i=[c_{pre_i-1}<c_{i-1}]

    于是我们得到了 O(n)O(n) 做法。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int a[500010],d[500010],p[500010],c[500010];
    bool f[500010];
    
    int main(){
        int t,n,ans;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);ans=0;
            memset(d,0,sizeof(d));
            for(int i=1;i<=n;i++)
                scanf("%d",a+i),p[i]=d[a[i]],d[a[i]]=i;
            for(int i=1;i<=n;i++){
                f[i]=(c[p[i]-1]<c[i-1]||!p[i]);
                c[i]=c[i-1]+(int)f[p[i]];
    			if(f[p[i]])ans=i;
            }
    		printf("%d\n",ans);
        }
        return 0;
    }
    
    • 1

    信息

    ID
    10732
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者