HDU 5371 Hotaru's problem 2015多校联合训练赛7

Hotaru's problem

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 243    Accepted Submission(s): 61


Problem Description
Hotaru Ichijou recently is addicated to math problems. Now she is playing with N-sequence.
Let's define N-sequence, which is composed with three parts and satisfied with the following condition:
1. the first part is the same as the thrid part,
2. the first part and the second part are symmetrical.
for example, the sequence 2,3,4,4,3,2,2,3,4 is a N-sequence, which the first part 2,3,4 is the same as the thrid part 2,3,4, the first part 2,3,4 and the second part 4,3,2 are symmetrical.

Give you n positive intergers, your task is to find the largest continuous sub-sequence, which is N-sequence.
 

Input
There are multiple test cases. The first line of input contains an integer T(T<=20), indicating the number of test cases. 

For each test case:

the first line of input contains a positive integer N(1<=N<=100000), the length of a given sequence

the second line includes N non-negative integers ,each interger is no larger than  109  , descripting a sequence.
 

Output
Each case contains only one line. Each line should start with “Case #i: ”,with i implying the case number, followed by a integer, the largest length of N-sequence.

We guarantee that the sum of all answers is less than 800000.
 

Sample Input
  
  
1 10 2 3 4 4 3 2 2 3 4 4
 

Sample Output
  
  
Case #1: 9
 

Source

用manacher算法算出每个分割位置的最长回文子串。

然后枚举每个分割位置

分析题目得到的答案串是

AB|BA|AB   就是两个回文串的拼接,有两个对称中心

对于每个位置i,回文串长度len[i],在[i,i+len[i]]中寻找满足 j-len[j] <= i的最大的j。

那么得到的一组解就是 min(len[i], j-i)

由于manacher算法是在每个位置插入一个符号,所有只计算偶数位即可,偶数位就是分割位置。

用线段树维护查找区间最大解即可。

线段树的结点表示这个位置的j-len[j]



#include<cstring>
#include<cstdio>
#include<string>
#include <iostream>
#include<algorithm>
using namespace std;
#define maxn 800007
int lc[maxn],rc[maxn],val[maxn];

int rad[maxn];
int tmp[maxn];
int s[maxn];
int inf = 1000000000;
void manacher(int n){//求回文串
    int cnt = 0;
    memset(rad,0,sizeof(rad));
    for(int i=0;i<n;i++){
        s[cnt++] = inf;
        s[cnt++] = tmp[i];
    }
    s[cnt++] = inf;
    n = cnt;
    int i=0,j=1,k;
    while(i<n){
        while(i-j>=0 && i+j<n && s[i-j]==s[i+j])
            j++;
        rad[i]=j-1;
        k=1;
        while(k<=rad[i] && rad[i]-k!=rad[i-k]){
            rad[i+k]=min(rad[i-k],rad[i]-k);
            k++;
        }
        i+=k;
        j=max(j-k,0);
    }
}
int tcnt;
//建树,奇数位就是原串的位置,不能作为分割点
void build(int u,int l,int r){
    if(l == r){
        if(l & 1) val[u] = inf;
        else val[u] = l - rad[l];
        return ;
    }
    lc[u] = tcnt++;
    rc[u] = tcnt++;
    int mid = (l+r)/2;
    build(lc[u],l,mid);
    build(rc[u],mid+1,r);
    val[u] = min(val[lc[u]],val[rc[u]]);

}
//查询在L,R区间满足小于等于p的最右边的位置
int query(int u,int l,int r,int L,int R, int p){
    if(l == r) return l;
    int mid = (l+r)/2;
    if(l == L && R == r){
        if(val[u] > p) return inf;
        if(val[rc[u]] <= p)
            return query(rc[u],mid+1,r,mid+1,R,p);
        else if(val[lc[u]] <= p) return query(lc[u],l,mid,l,mid,p);
        else return inf;
    }

    if(mid < L)
        return query(rc[u],mid+1,r,L,R,p);
    else if(mid >= R)
        return query(lc[u],l,mid,L,R,p);
    else {
        int ans = query(rc[u],mid+1,r,mid+1,r,p);
        if(ans != inf) return ans;
        return query(lc[u],l,mid,L,mid,p);
    }
    return inf;
}


void init(){
    tcnt = 2;
    memset(rc,0,sizeof(rc));
    memset(lc,0,sizeof(lc));
    memset(val,0x3f,sizeof(val));
}
int main()
{
    int t,tt=1,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 0;i < n; i++)
            scanf("%d",&tmp[i]);
        manacher(n);
        n = n*2+1;
        init();
        build(1,0,n-1);

        int ans = 0;
        for(int i = 0;i < n; i+=2){ //枚举分割位置
            int p = i + rad[i];
            int q = query(1,0,n-1,i,p,i);
            if(q == inf) continue;
            int res = min(rad[i],q-i);
            ans = max(ans,res/2);//manacher的长度增加了一倍,所有要/2
        }
        printf("Case #%d: %d\n",tt++,ans*3);
    }
    return 0;
}



/*
22
10
2 3 4 4 3 2 2 3 4 4
20
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
12
1 1 2 2
2 2 1 1
1 1 2 2
*/


















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值