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 T\le 10^41T104
∑n≤2×105\sum n \le 2\times 10^5n2×105
1≤ai≤2001\le 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;
}
                                                      

猜你喜欢

转载自www.cnblogs.com/lipoicyclic/p/12700048.html
今日推荐