zoukankan      html  css  js  c++  java
  • jzoj6008. 【THUWC2019模拟2019.1.18】Sequence (矩阵加速)

    题面

    茉优最近研究发现,一个人的想愿能力可以认为是字符串S的一个子串S[l,r],而连接值可以认为是这个子串的本质不同子序列个数。现在她想验证她的结论是否正确,于是她给了你Q个询问,希望你帮她来计算,注意空串也是子序列。

    题解

    考场上暴力都打错

    先考虑暴力,设(f_i)(i)下标为终止位置的子序列个数,那么(f_i)就等于前面的所有(f_j,j<i)的和,不过要减去所有(s_j=s_i)(f_j),否则会重复

    然后考虑把(f_i)给前缀和,先离散化,设一个向量,其中(A_i)表示以(i)这个值为结尾的子序列个数,最后一个值表示空集,那么转移矩阵就是其它都和单位矩阵一样,第(s_i)列全为(1)

    然后考虑转移矩阵的逆矩阵,就是其它和单位矩阵一样,第(s_i)列除主对角线上全为(-1),主对角线上为(1)

    那么,只要我们能够维护(B_1B_2...B_i),,记为(h_i),以及(B^{-1}_iB^{-1}_{i-1}...B^{-1}_1),记为(c_i)对于询问,我们就可以快速表示了,为([0,0,0,...,1] imes h_{l-1} imes c_r)

    如何维护前缀积?以(h_i)为例,发现一个矩阵左乘上(B_i),就是其它列都不变,第(s_i)列的每一个数都变为这一行所有元素的和,动态维护每行的元素和并单列修改即可。而一个矩阵右乘上(B^{-1}_i),第(s_i)行不变,其它列每个元素(A_{i,j})都减去(A_{s_i,j}),可以打个(tag)

    然而这样是(O(53^2|S|))

    考虑([0,0,0,...,1] imes h_{l-1}),最终结果就是(h_{i-1})的最下面一行,你们我们对于每个位置的(h)只要记录最下面一行就好了。后面要乘上(c_r),因为我们最终需要的是整个向量所有数的和,发现每一项都乘了对应行的和,所以每个位置只要记录对应行的和就行了

    复杂度(O(53|S|))

    ///minamoto
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
    #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
    inline int max(const R int &x,const R int &y){return x>y?x:y;}
    inline int min(const R int &x,const R int &y){return x<y?x:y;}
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res=1,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=1e6+5,M=52,P=998244353;
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
        R int res=1;
        for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
        return res;
    }
    int h[N][M+5],c[N][M+5],A[M+5][M+5],tag[M],sum[M];
    char s[N];
    int a,b,p,q,r,x,y,z,Q,n,la,lb;
    inline int calc(R char ch){return ch>='a'?ch-'a'+26:ch-'A';}
    int main(){
         freopen("sequence.in","r",stdin);
     	freopen("sequence.out","w",stdout);
        scanf("%s",s+1),n=strlen(s+1);
        scanf("%d%d%d%d%d%d",&Q,&a,&b,&p,&q,&r);
        fp(i,0,M)A[i][i]=sum[i]=1;
        fp(i,1,n){
            int v=calc(s[i]);
            fp(j,0,M){
                sum[j]=dec(sum[j],A[j][v]),A[j][v]=add(A[j][v],sum[j]);
                sum[j]=add(sum[j],A[j][v]);
                h[i][j]=sum[j];
            }
        }
        memset(A,0,sizeof(A));
        fp(i,0,M)A[i][i]=1;
        fp(i,1,n){
            int v=calc(s[i]);
            fp(j,0,M){
            	A[v][j]=dec(A[v][j],tag[j]);
                tag[j]=add(tag[j],A[v][j]);
                A[v][j]=add(A[v][j],tag[j]);
                c[i][j]=dec(A[M][j],tag[j]);
            }
        }
        while(Q--){
    		la=a,lb=b;
    		a=(1ll*la*p+1ll*lb*q+z+r)%P;
    		b=(1ll*lb*p+1ll*la*q+z+r)%P;
    		x=min(a%n,b%n)+1,y=max(a%n,b%n)+1;
    		if(x==1)z=h[y][M];
    		else{
    		    z=0;
    		    fp(i,0,M)z=add(z,mul(c[x-1][i],h[y][i]));
    		}
    // 		printf("QAQ %d %d %d
    ",x,y,z);
    	}
    	printf("%d
    ",z);
    	return 0;
    }
    
  • 相关阅读:
    c# 代码控制文件夹权限时,只显示特殊权限的问题
    C#使用SQLite数据库遇到的问题(二)
    GC工作原理
    Thread
    -static
    String 练习题
    更进ATM
    继承 示例
    数组
    求奇偶数
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10293694.html
Copyright © 2011-2022 走看看