20181229模拟 T1 palindrome

20181229模拟 T1 palindrome

题意 :

\(S\)是字符串\(s\)的子串可重集,求\(\sum\limits_{x\in S}\sum\limits_{y\in S}(|x|+|y|)\times [xy\ is \ palidrome]mod\ 2013265921\)。

分析:

\(2013265921\)的原根是\(31\),所以这道题我使用后缀自动机+回文树来解决。

注意到一个由两个字符串所组成的回文串\(xy\),不妨设\(|x|<|y|\),\(y\)显然是由一个回文串和一个\(x\)的反串组成。

于是我们可以枚举回文串的结尾\(i\),显然向右能被反串匹配的是一段区间,向左能匹配的所有回文串就是一直跳回文树上\(fail\)能到达的那些结点,求出此时向右匹配反串的种类\(c1\)和总长度\(s1\)向左匹配回文串的个数\(c2\)和总长度\(s2\),那么答案就是$\sum\limits_{i=1}^{n-1}c1_{i+1}\times s2_i+c2_i\times s1_{i+1} $。 然后这两个用后缀自动机+回文树即可完美解决。

然后不要忘记处理回文串在右反串在左的情况,我的做法是将整个字符串反过来重新求一遍。

最后需要加上回文串长度为\(0\)的方案。

code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 1000050
#define mem(x) memset(x,0,sizeof(x))
#define mod 2013265921
#define db(x) cerr<<#x<<" = "<<x<<endl
typedef long long ll;
int n;
char w[N];
ll s1[N],c1[N],c2[N],s2[N];
ll ss(ll l,ll r) {
    return (l+r)*(r-l+1)/2%mod;
}
struct Sam {
    int ch[N][26],fa[N],len[N],lst,cnt,ke[N],ro[N],siz[N];
    ll sum[N],sd[N];
    void init() {
        lst=cnt=1;
    }
    void insert(int x) {
        int p=lst,np=++cnt,q,nq; lst=np;
        len[np]=len[p]+1;
        for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if(!p) fa[np]=1;
        else {
            q=ch[p][x];
            if(len[q]==len[p]+1) fa[np]=q;
            else {
                nq=++cnt; len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q]; fa[q]=fa[np]=nq;
                for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            }
        }
        siz[lst]++;
    }
    void kero() {
        int i;
        for(i=1;i<=cnt;i++) ke[len[i]]++;
        for(i=1;i<=cnt;i++) ke[i]+=ke[i-1];
        for(i=cnt;i;i--) ro[ke[len[i]]--]=i;
        for(i=cnt;i>=1;i--) siz[fa[ro[i]]]+=siz[ro[i]];
        for(i=2;i<=cnt;i++) {
            int p=ro[i];
            sd[p]=(sd[fa[p]]+siz[p]*(len[p]-len[fa[p]]))%mod;
            sum[p]=(sum[fa[p]]+siz[p]*ss(len[fa[p]]+1,len[p]))%mod;
        }
    }
    void pipei() {
        int p=1,now=0,i;
        for(i=n;i;i--) {
            int x=w[i];
            if(ch[p][x]) {
                p=ch[p][x]; now++;
            }else {
                for(;p&&!ch[p][x];p=fa[p]) ;
                if(!p) {
                    p=1; now=0;
                }else {
                    now=len[p]+1; p=ch[p][x];
                }
            }
            s1[i]=(sum[fa[p]]+siz[p]*ss(len[fa[p]]+1,now))%mod,c1[i]=(siz[p]*(now-len[fa[p]])+sd[fa[p]])%mod;
        }
    }
    void clear() {
        mem(ch);mem(fa);mem(len);mem(ke);mem(ro);mem(siz);mem(sum);mem(sd);
        init();
    }
}sam;
struct Pam {
    int ch[N>>1][26],fail[N],len[N],cnt,lst,dep[N];
    ll sum[N];
    void init() {
        len[1]=-1; fail[0]=fail[1]=1; cnt=1; lst=0;
    }
    void insert(int i,int x) {
        int p=lst,np;
        for(;w[i-len[p]-1]!=x;p=fail[p]) ;
        if(!ch[p][x]) {
            np=++cnt;
            len[np]=len[p]+2;
            int q=fail[p];
            for(;w[i-len[q]-1]!=x;q=fail[q]) ;
            fail[np]=ch[q][x];
            ch[p][x]=np;

            dep[np]=dep[fail[np]]+1;
            sum[np]=(sum[fail[np]]+len[np])%mod;
        }
        lst=ch[p][x];
    }
    void wk() {
        int i;
        for(i=1;i<=n;i++) {
            insert(i,w[i]);
            c2[i]=dep[lst];
            s2[i]=sum[lst];
        }
    }
    void clear() {
        mem(ch);mem(fail);mem(len);mem(dep);mem(sum);
        init();
    }
}pam;
int main() {
    scanf("%s",w+1); n=strlen(w+1);
    w[0]=29;
    int i;
    ll ans=0;
    for(i=1;i<=n;i++) w[i]-='a';
    sam.init(); pam.init();
    for(i=1;i<=n;i++) sam.insert(w[i]);
    sam.kero();
    sam.pipei();
    pam.wk();
    for(i=1;i<=n;i++) s1[i]*=2;
    for(i=1;i<=n;i++) {
        ans+=(s1[i]*c2[i-1]+c1[i]*s2[i-1])%mod;
    }
    sam.clear();
    pam.clear();
    mem(s1);mem(s2);mem(c1);mem(c2);

    reverse(w+1,w+n+1);
    for(i=1;i<=n;i++) sam.insert(w[i]);
    sam.kero();
    sam.pipei();
    pam.wk();
    for(i=1;i<=n;i++) s1[i]*=2;
    for(i=1;i<=n;i++) {
        ans+=(s1[i]*(c2[i-1]+1)+c1[i]*s2[i-1])%mod;
    }
    printf("%lld\n",(ans+mod)%mod);
}

原文地址:https://www.cnblogs.com/suika/p/10240431.html

时间: 2024-11-09 06:57:06

20181229模拟 T1 palindrome的相关文章

2017-9-3 校内模拟T1卡片card

题意:有三种字母,可以用两个不同的换一个第三种字母,两个相同的换一个同种字母(即消去一个),问最后剩下的字母. 第一题显然是if题,总体来说只有三种情况: 1.有三种不同的>>输出"BGR" 2.有两种不同的:{ (1)两种都有超过一个>>情况1: (2)一种只有一个>>输出这种和未出现的字母 (3)两种都只有一个>>输出未出现的字母 }   3.只有一种字母>>输出这个字母 代码(极丑): 1 #include<ios

APIO 2014

练习赛,评测的时候好像出了些问题,最后我拿自己机子测的212/300,第二题负责评测的写的SPJ就判了第一行的答案,不知道有没出什么问题. 先贴代码 T1.palindrome 92/100 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; #define MN 300000 #

luogu P1843 奶牛晒衣服

模拟T1,贪心+排序或者二分都行 贪心策略很好想,显然每次晒耗时最久的衣服最优,问题是要在每次晒完后都再次找到耗时最久的衣服,不能每次都sort,所以单调队列或者大根堆 二分也不难,直接二分时间,筛一遍衣服统计需要烘干的时间然后判断是否满足就行 模拟的时候敲了贪心+排序,所以这里放二分的代码(滑稽),个人感觉贪心更好写 #include<cstdio> #include<algorithm> #include<iostream> using namespace std;

Day1:T1 模拟 T2 拓扑排序

T1:模拟 自己第一天的简直跟白痴一样啊...模拟都会打错.. 当时貌似在更新最大值的时候打逗比了... if((sum[x]==max && x<maxh) || sum[x]>max){  max=sum[x];  maxh=x; //现在(也就是9月+)再看,脑袋里只有sortsortsort,连最基本的更新最大指都忘了....智商唉.... 附上代码: #include<cstdio> #include<cstring> using namesp

UVA 12050 - Palindrome Numbers 模拟

题目大意:给出i,输出第i个镜像数,不能有前导0. 题解:从外层开始模拟 #include <stdio.h> int p(int x) { int sum, i; for(sum=i=1;i<=x;i++) sum *= 10; return sum; } int main() { int n, i, j, t, cs[1000], c; while(~scanf("%d", &n)) { if(n==0) break; i=1; while(n>9*

SRM11 T1 骰子 (模拟--&gt;数学)

题目大意:一个骰子在有R*C格的矩形地图上从第一行第一格开始滚来滚去,滚完一行后以相反方向滚下一行,滚完所有格子后停.求所有时刻骰子上方的点数和. O(RC)做法:模拟.用u,f,r分别记录骰子上.前.右三面的点数. 向左滚:int d=7-u:u=r:r=d;//骰子相对面上点数和为7,d暂存底面点数,滚动后原来的右面跑到上面,底面跑到右面. 向右滚:int l=7-r:r=u; u=l;//l暂存左面点数 向下滚:int b=7-f:f=u; u=b;//b暂存后面点数 O(1)做法:数学(

汕头市队赛 SRM10 T1模拟只会猜题意

模拟只会猜题意 SRM 10 描述 有一本n个单词的词典,求按下列方法能造出的不相同的词汇数目.  1.词典中的单词是一个词. 2.能分为两部分的,其中前一部分是一个词典词或者其非空前缀,后一部分是一个词典词或者其非空后缀的词. 输入格式 第一行一个整数n,接下来n行每行一个字符串,表示单词 输出格式 一个整数,答案 样例输入 3 a ab bc 样例输出 20 数据范围与约定 对10%的数据,n=1 对另外40%的数据,1<=n<=10,单词只含字符'a' 对所有的数据,1<=n<

2017.9.23 NOIP2017 金秋杯系列模拟赛 day1 T1

回形遍历( calc .cpp/c/pas) 时间限制:1s内存 限制: 256MB [问题 描 述]给出一个 n*m 的棋盘,按如下方式遍历,请问(x,y)往后 z 步走到的是哪个格子. [输入]输入文件名为 calc.in.一行,包含五个整数:n,m,x,y,z[输出]输出文件名为 calc.out.输出一行,包含两个整数,表示所在格子的横纵坐标[输入输出样例] calc .in calc .out 4 5 3 0 5 2 4 [ 样例解释 ] [数据说明]对于 70%的数据,1<=n,m,

Reverse Integer - Palindrome Number - 简单模拟

第一个题目是将整数进行反转,这个题实现反转并不难,主要关键点在于如何进行溢出判断.溢出判断再上一篇字符串转整数中已有介绍,本题采用其中的第三种方法,将数字转为字符串,使用字符串比较大小的方法进行比较. 代码如下: 1 class Solution { 2 public: 3 int reverse(int x) { 4 int stand[10]={2,1,4,7,4,8,3,6,4,8}; 5 int getnum[10]; 6 int a=0,flag=0; 7 if(x>0) 8 flag