• Codeforces Round #634 E2. Three Blocks Palindrome (hard version)(双指针/前缀和/二分/好题)


    题目描述

    The only difference between easy and hard versions is constraints.

    You are given a sequence a a a consisting of n n n positive integers.

    Let's define a three blocks palindrome as the sequence, consisting of at most two distinct elements (let these elements are a a a and b b b , a a a can be equal b b b ) and is as follows: [a,a,…,a⏟x,b,b,…,b⏟y,a,a,…,a⏟x] [underbrace{a, a, dots, a}_{x}, underbrace{b, b, dots, b}_{y}, underbrace{a, a, dots, a}_{x}] [xa,a,,a,yb,b,,b,xa,a,,a] . There x,y x, y x,y are integers greater than or equal to 0 0 0 . For example, sequences [] [] [] , [2] [2] [2] , [1,1] [1, 1] [1,1] , [1,2,1] [1, 2, 1] [1,2,1] , [1,2,2,1] [1, 2, 2, 1] [1,2,2,1] and [1,1,2,1,1] [1, 1, 2, 1, 1] [1,1,2,1,1] are three block palindromes but [1,2,3,2,1] [1, 2, 3, 2, 1] [1,2,3,2,1] , [1,2,1,2,1] [1, 2, 1, 2, 1] [1,2,1,2,1] and [1,2] [1, 2] [1,2] are not.

    Your task is to choose the maximum by length subsequence of a a a that is a three blocks palindrome.

    You have to answer t t t independent test cases.

    Recall that the sequence t t t is a a subsequence of the sequence s s s if t t t can be derived from s s s by removing zero or more elements without changing the order of the remaining elements. For example, if s=[1,2,1,3,1,2,1] s=[1, 2, 1, 3, 1, 2, 1] s=[1,2,1,3,1,2,1] , then possible subsequences are: [1,1,1,1] [1, 1, 1, 1] [1,1,1,1] , [3] [3] [3] and [1,2,1,3,1,2,1] [1, 2, 1, 3, 1, 2, 1] [1,2,1,3,1,2,1] , but not [3,2,3] [3, 2, 3] [3,2,3] and [1,1,1,1,2] [1, 1, 1, 1, 2] [1,1,1,1,2] .

    输入格式

    The first line of the input contains one integer t t t ( 1≤t≤104 1 le t le 10^4 1t104 ) — the number of test cases. Then t t t test cases follow.

    The first line of the test case contains one integer n n n ( 1≤n≤2⋅105 1 le n le 2 cdot 10^5 1n2105 ) — the length of a a a . The second line of the test case contains n n n integers a1,a2,…,an a_1, a_2, dots, a_n a1,a2,,an ( 1≤ai≤200 1 le a_i le 200 1ai200 ), where ai a_i ai is the i i i -th element of a a a . Note that the maximum value of ai a_i ai can be up to 200 200 200 .

    It is guaranteed that the sum of n n n over all test cases does not exceed 2⋅105 2 cdot 10^5 2105 ( ∑n≤2⋅105 sum n le 2 cdot 10^5 n2105 ).

    输出格式

    For each test case, print the answer — the maximum possible length of some subsequence of a a a that is a three blocks palindrome.

    题意翻译

    定义 [a,a,…,a⏟x,b,b,…,b⏟y,a,a,…,a⏟x][underbrace{a, a, dots, a}_{x}, underbrace{b, b, dots, b}_{y}, underbrace{a, a, dots, a}_{x}][xa,a,,a,yb,b,,b,xa,a,,a] 这样的序列为TBP,其中 x,yx,yx,y 为自然数(注意 x,yx,yx,y 可以为0)。

    给出一个长为 nnn 的数列 aaa ,求它符合TBP定义的子序列的最长长度。
    TTT 组询问。

    1≤T≤1041 le Tle 10^41T104
    ∑n≤2×105sum n le 2 imes 10^5n2×105
    1≤ai≤2001le a_i le 2001ai200

    输入输出样例

    输入 #1
    6
    8
    1 1 2 2 3 2 1 1
    3
    1 3 3
    4
    1 10 10 1
    1
    26
    2
    2 1
    3
    1 1 1
    输出 #1
    7
    2
    4
    1
    1
    3
    给定三块回文串的定义,求一个序列里包含的最长三块回文串的长度。
    暴力做法是把原序列分成三块,枚举左右端点以及两边、中间部分的颜色。对于hard version过不了。
    首先,要预处理前缀和,使得我们能O(1)地查询l~r区间里x这个数出现的次数,sum[201][N]代表前缀和数组。然后我们先枚举左端点l,求出1~l里a[l]出现的次数cnt(直接查询),然后根据这个次数二分查找右端点,其中右端点r满足r~n区间包含cnt个a[l]。因为不是a[l]的数字之前已经计算过了,所以这里只需要关心新的a[l]即可。因为前缀和数组是单调的,满足二分查找的条件。这里注意很关键的一个地方,有可能有很多个右端点都满足r~n区间包含cnt个a[l],根据贪心的原则我们应该找到最靠右的一个端点,这样能使中间部分尽可能长。所以“前缀和“应该倒着求,配合upper_bound使用。upper_bound(起始地址,结束地址,要查找的数值) 返回的是数值 最后一个 出现的位置。
    这样左右端点确定下来以后,1~200枚举中间部分的数字,对出现次数取最大,再加上两边的数字的出现次数就是候选答案了,与ans比较更新ans即可。
    #include <bits/stdc++.h>
    using namespace std;
    int a[200005],n;
    int sum[202][200005]={0};
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
            int ans=1;//ans最小也为1 
            scanf("%d",&n);
            int i,j;
            for(i=1;i<=n;i++) scanf("%d",&a[i]); 
            for(i=0;i<=201;i++)
            {
                for(j=n+1;j>=1;j--)
                {
                    sum[i][j]=0;//清空前缀和数组 
                }
            }
            for(i=1;i<=200;i++)
            {
                for(j=n;j>=1;j--)
                {
                    sum[i][j]=sum[i][j+1]+(a[j]==i?1:0);//倒着存“前缀和”,对于每个j位置所有的i数字都要更新 
                }
            }
            int l,r;
            for(l=1;l<=n;l++)//枚举左端点 
            {
                int num=a[l];//枚举左端点 num是左端点代表的数 
                r=upper_bound(sum[num]+1,sum[num]+n+1,sum[num][1]-sum[num][l+1],greater<int>())-sum[num]-1;//sum数组第二维单调递减,用upper_bound() greater重载 找到最后一个sum数组值等于左区间a[l]出现次数的位置 
                if(!(r>l&&r<=n))continue;//右端点不是合法位置 
                int mid=0;//中间部分最大的出现次数 
                for(j=1;j<=200;j++)//枚举数字 
                {
                    mid=max(mid,sum[j][l+1]-sum[j][r]);//前缀和O(1)查询 
                }
                ans=max(ans,sum[num][r]*2+mid);//更新答案 
            }
            cout<<ans<<endl;
        }
        return 0;
    }
                                                          
  • 相关阅读:
    势函数的构造
    10.29模拟赛总结
    10.29vp总结
    10.25模拟赛总结
    10.24模拟赛总结
    线段树练习
    一键挖矿
    P1972 [SDOI2009]HH的项链
    P3901 数列找不同
    P5546 [POI2000]公共串
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/12700048.html
Copyright © 2020-2023  润新知