BZOJ 2160 拉拉队排练 Manacher + 前缀和

题目大意:

就是现在给出一个长度为 n 的字符串(1 <= n <= 10^6)和一个正整数K(1 <= K <= 10^12)

对于给出的长度为n的字符串如果其回文串的数量比K少则输出-1, 否则输出所有回文串中长度为奇数的最长的前K个回文串的长度的乘积, 结果对于19930726取模输出


大致思路:

首先可以用manacher算法确定每个位置的回文半径, 由于这里只需要长度是奇数, 所以不需要再原来的字符串的相邻两个字符之间插入未出现的字符, 直接在首尾添加好不同字符之后盘一遍manacher算法, 对于位置i为中心的回文半径R[i], 用dp[i]来表示相邻长度的回文串的数量差分(其实就是一个常用的前缀和技巧, 因为这里每次更新[1, R[i]]这个区间 + 1, 而只在所有更新完毕之后才查询所以没有必要使用树状数组, 直接根据每次更新的时候dp[1]++, dp[R[i] + 1]--, 最后后dp[1~i]的和就是最终ans[i]的值, 即长度为2*i - 1的回文串的数量

然后用快速幂就可以了, 没有什么难度

细节就看代码吧

(吐槽一下第一次在BZOJ上交题没在F.A.Q里看到用%I64d还是%lld, 然后我PE了看了好久不知道为什么....最后还是用%lld过了 = =)

话说用%I64d就算是Wrong Answer也判了Presentation Error..


代码如下:

Result  :  Accepted     Memory  :  21780 KB     Time  :  520 ms

/**************************************************************
    Problem: 2160
    User: Gatevin
    Language: C++
    Result: Accepted
    Time:520 ms
    Memory:21780 kb
****************************************************************/
 
/*
 * Author: Gatevin
 * Created Time:  2015/3/20 11:20:33
 * File Name: Chitoge_Kirisaki.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
 
#define maxn 1000100
 
const lint mod = 19930726;
 
int n;
lint K;
char in[maxn];
int R[maxn];
lint dp[maxn];
lint ans[maxn];
/*
 * 这里没有在输入的字符串中间插入特殊字符
 * 得到的半径R都是奇数长度的回文串的半径
 */
void Manacher(char *s, int *R, int n)
{
    int p = 0, mx = 0;
    R[0] = 1;
    for(int i = 1; i <= n; i++)
    {
        if(mx > i)
            R[i] = min(R[2*p - i], mx - i);
        else R[i] = 1;
        while(s[i - R[i]] == s[i + R[i]])
            R[i]++;
        if(i + R[i] > mx)
            p = i, mx = i + R[i];
    }
    return;
}
 
lint quick_pow(lint base, lint pow)
{
    lint ret = 1;
    while(pow)
    {
        if(pow & 1) ret = (ret*base) % mod;
        pow >>= 1;
        base = base*base % mod;
    }
    return ret;
}
 
int main()
{
    scanf("%d %lld", &n, &K);
    {
        scanf("%s", in + 1);
        in[0] = '@'; in[n + 1] = '$'; in[n + 2] = '\0';
        Manacher(in, R, n);
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)//所有以i为中心的奇数长度的回文串
        {
            dp[1]++;
            dp[R[i] + 1]--;
        }
        memset(ans, 0, sizeof(ans));
        int maxlen = 0;
        for(int i = 1; i <= (n + 1) >> 1; i++)//ans[i]表示长度为2*i - 1的串的个数
        {
            ans[i] = ans[i - 1] + dp[i];
            if(ans[i] > 0) maxlen = i;
        }
        lint result = 1;
        int r = maxlen;
        while(r && K > 0)
            result = (result*quick_pow((lint)(2*r - 1), min(ans[r], K))) % mod, K -= ans[r], r--;
        if(K > 0) printf("-1\n");
        else printf("%lld\n", result);
    }
    return 0;
}


### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值