[JZOJ5132][SDOI省队集训2017]子序列

298人阅读 评论(0) 收藏 举报
分类:

题目大意

字符集为9的字符串。
若干次询问区间本质不同的子序列个数。

考虑暴力

子序列要求本质不同。
假如每个位置向后每种字符的第一个位置连单向边。
设置一个虚点向区间每种字符第一个位置连单向边。
那么就是这个虚点出发有多少走法。
用另一种形式,你设f[i,j]表示最后走到了i,是从区间j第一次出现的位置开始走的,有多少走法。
你考虑i左移一格如何更新,发现
对于新的i,有f[i][a[i]]=1。
对于其他的i,有f[i][a[i]]=9j=1f[i][j]
假如当前做到l,[l,r]的答案是
ri=l9j=1f[i][j]
我们继续用另一种形式表达如何计算[l,r]的答案。
有一个长度为9的ans数组,ans[i]=rj=lf[j][i]
它初始全0。接下来扫[l,r]的每一位,扫到i时做下列变化
ans[a[i]]=1+9j=1ans[j]
最后的答案显然是
9j=1ans[j]

考虑优化

用一个1*10的矩阵存下ans,最后加一个常数1。
于是每次ans的变化可以用10*10的矩阵表示。
于是第i个位置有一个矩阵A[i]。
只要我们能求得[l,r]的A的积,就可以得到答案。
我们发现这个矩阵是可逆的,可以求出B[i]表示逆矩阵。
那么只要计算
B[l1]B[l2]B[1]A[1]A[r]
即可。
于是可以预处理前缀和。
注意每次询问不要先算两个10*10矩阵的积,再与1*10矩阵相乘,直接用1*10矩阵乘过去即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10,mo=1000000007;
int a[9][10][10],b[9][10][10],sum[maxn][10][10],num[maxn][10][10],o[10][10],c[10],d[10];
char s[maxn];
int i,j,k,l,r,t,n,m,ans;
bool czy;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
void getb(int x){
    int i,j,k,t;
    fo(i,0,9)
        b[x][i][i]=1;
    fo(i,0,9){
        fo(j,i,9)
            if (a[x][j][i]){
                fo(k,i,9){
                    swap(a[x][j][k],a[x][i][k]);
                    swap(b[x][j][k],b[x][i][k]);
                }
                break;
            }
        fo(j,i+1,9){
            t=(ll)a[x][j][i]*qsm(a[x][i][i],mo-2)%mo;
            fo(k,i,9){
                (a[x][j][k]-=(ll)a[x][i][k]*t%mo)%=mo;
                (b[x][j][k]-=(ll)b[x][i][k]*t%mo)%=mo;
            }
        }
    }
    fd(i,9,0){
        fo(j,i+1,9)
            if (a[x][i][j]){
                fo(k,i,9){
                    (a[x][i][k]-=a[x][j][k])%=mo;
                    (b[x][i][k]-=b[x][j][k])%=mo;
                }
            }
    }
}
int main(){
    freopen("sub.in","r",stdin);freopen("sub.out","w",stdout);
    scanf("%s",s+1);
    n=strlen(s+1);
    fo(i,0,8){
        fo(j,0,9) a[i][j][j]=1;
        fo(j,0,9) a[i][j][i]=1;
        getb(i);
        fo(j,0,9)
            fo(k,0,9)
                a[i][j][k]=0;
        fo(j,0,9) a[i][j][j]=1;
        fo(j,0,9) a[i][j][i]=1;
    }
    /*fo(i,0,9)
        fo(j,0,9)
            o[i][j]=0;
    fo(k,0,9)
        fo(i,0,9)
            fo(j,0,9)
                (o[i][j]+=(ll)a[1][i][k]*b[1][k][j]%mo)%=mo;
    czy=1;
    fo(i,0,9)
        fo(j,0,9)
            if (o[i][j]!=(i==j?1:0)){
                czy=0;
                break;
            }
    printf("%d\n",czy);*/
    fo(i,0,9) sum[0][i][i]=num[0][i][i]=1;
    fo(l,1,n){
        t=s[l]-'a';
        fo(i,0,9)
            fo(j,0,9)
                o[i][j]=0;
        fo(k,0,9)
            fo(i,0,9)
                fo(j,0,9)
                    (o[i][j]+=(ll)sum[l-1][i][k]*a[t][k][j]%mo)%=mo;
        fo(i,0,9)
            fo(j,0,9)
                sum[l][i][j]=o[i][j];
        fo(i,0,9)
            fo(j,0,9)
                o[i][j]=0;
        fo(k,0,9)
            fo(i,0,9)
                fo(j,0,9)
                    (o[i][j]+=(ll)b[t][i][k]*num[l-1][k][j]%mo)%=mo;
        fo(i,0,9)
            fo(j,0,9)
                num[l][i][j]=o[i][j];
    }
    m=read();
    while (m--){
        l=read();r=read();
        fo(i,0,9) c[i]=d[i]=0;
        c[9]=1;
        fo(k,0,9)
            fo(j,0,9)
                (d[j]+=(ll)c[k]*num[l-1][k][j]%mo)%=mo;
        fo(i,0,9) c[i]=d[i],d[i]=0;
        fo(k,0,9)
            fo(j,0,9)
                (d[j]+=(ll)c[k]*sum[r][k][j]%mo)%=mo;
        ans=0;
        fo(i,0,8) (ans+=d[i])%=mo;
        (ans+=mo)%=mo;
        printf("%d\n",ans);
    }
}
查看评论

SOPJ第一题

python新手,找了个支持检查python代码的
  • u010680933
  • u010680933
  • 2014-10-20 23:06:50
  • 205

tjut 4927

import java.math.BigInteger;   import java.util.*;   import java.io.*;      public class Main {     ...
  • luozhong11
  • luozhong11
  • 2016-07-24 09:41:14
  • 134

[bzoj4927][SDOI省队集训2017]diyiti

题目大意在一个长度为n的数列中找出六个数,使得可以把它们分成四组,每组的和均相等。求方案数。XJB做只有1+1+1+3或1+1+2+2两种拆分。 先预处理two[x]表示找出两个数和为x的方案数,以...
  • WerKeyTom_FTD
  • WerKeyTom_FTD
  • 2017-05-31 20:26:16
  • 453

[ DP 莫队 ]「2017 山东一轮集训 Day6」LOJ#6074 子序列

题解 #include&amp;lt;bits/stdc++.h&amp;gt; using namespace std; const int SZ=1&amp;lt;&amp;lt;25; ...
  • gjghfd
  • gjghfd
  • 2018-03-13 15:26:50
  • 63

[JZOJ5131][SDOI省队集训2017]距离

题目描述点分治询问拆成四条到根的询问。 bfs一遍,处理出可持久化点分树。 查询直接查。#include #include #include #define fo(i,a,b) for(i=a;i...
  • WerKeyTom_FTD
  • WerKeyTom_FTD
  • 2017-06-09 22:18:30
  • 208

[最短路 杂题] LOJ#6075. 「2017 山东一轮集训 Day6」重建

刚开始以为可以二分…实际上是没有单峰性的。考虑每条边都加一个整数,那么肯定是使 ss 到 tt 的路劲经过的点变少。用DP算出经过 ii 个点到达 jj 的最短路,用 gi,jg_{i,j} 表示,以...
  • Coldef
  • Coldef
  • 2017-10-12 14:45:41
  • 240

SDOI2016第一轮省队集训

233
  • sxb_201
  • sxb_201
  • 2016-05-30 21:23:48
  • 1022

[计数][容斥] LOJ#6065 || BZOJ4927 && 2017 山东一轮集训 Day3. 第一题

因为要选6根木棒,发现肯定是1,1,2,2或1,1,1,3形式。 可以枚举2和3的部分,然后推一推,容斥容斥就可以了 但是细节贼多#include #include #include #in...
  • Coldef
  • Coldef
  • 2017-07-11 17:48:12
  • 483

【清华集训2017模拟12.10】回文串

DescriptionNYG 很喜欢研究回文串问题,有一天他想到了这样一个问题: 给出一个字符串 S,现在有 4 种操作: • addl c :在当前字符串的左端加入字符 c; • addr c...
  • alan_cty
  • alan_cty
  • 2017-12-09 21:39:36
  • 248

「2016山东省队集训」 Play with array

DescriptionInputOutputSample Input7 6 6 2 7 4 2 5 7 1 3 6 2 2 4 2 2 4 6 2 2 3 3 6 1 2 6 1 1 ...
  • hydhyd2012
  • hydhyd2012
  • 2017-01-05 08:56:27
  • 138
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 39万+
    积分: 1万+
    排名: 1456
    最新评论
    文章分类