[莫队维护DP] LOJ#6074. 「2017 山东一轮集训 Day6」子序列
2017-10-12 18:20
465 查看
如果只考虑求一个数列的不同子序列个数,可以简单地DP出来
fi=2×fi−1−flstai−1,其中 lstj 表示字符 j 上次出现的位置,没出现过就忽略
但是这样不好维护
可以换一种DP方式,fi,j 表示以字符 i 开头,字符 j 结尾的子序列个数。
那么在左边新加一个字符 x ,fx,i 会加上 ∑j≠xfj,i ,考虑以 x 开头的子序列,去掉开头的所有连续的 x 后的子序列个数就是 ∑j≠xfj,i ,然后这些子序列在开头加一些 x 是原本的 fx,i ,在加上当前的 x 就都是未出现过的了…
这话说的我自己都看不懂
大概就这样可以用莫队维护
然后就被卡常了……
学了一下wxh的莫队打法
然后就过了…
fi=2×fi−1−flstai−1,其中 lstj 表示字符 j 上次出现的位置,没出现过就忽略
但是这样不好维护
可以换一种DP方式,fi,j 表示以字符 i 开头,字符 j 结尾的子序列个数。
那么在左边新加一个字符 x ,fx,i 会加上 ∑j≠xfj,i ,考虑以 x 开头的子序列,去掉开头的所有连续的 x 后的子序列个数就是 ∑j≠xfj,i ,然后这些子序列在开头加一些 x 是原本的 fx,i ,在加上当前的 x 就都是未出现过的了…
这话说的我自己都看不懂
大概就这样可以用莫队维护
然后就被卡常了……
学了一下wxh的莫队打法
friend bool operator <(const QQ &a,const QQ &b){ return pos[a.l]<pos[b.l] || (pos[a.l]==pos[b.l] && (pos[a.l]&1 ? a.r<b.r : a.r>b.r)); }
然后就过了…
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <vector> #include <cmath> #include <ctime> using namespace std; typedef long long ll; const int N=100010,P=1e9+7; int n,q,S; ll b[15][15]; int a ; int ans ; int pos ; struct QQ{ int l,r,g; friend bool operator <(const QQ &a,const QQ &b){ return pos[a.l]<pos[b.l] || (pos[a.l]==pos[b.l] && (pos[a.l]&1?a.r<b.r:a.r>b.r)); } }Q ; ll cur; inline void add(ll &x,ll y){ (x+=y)%=P; } inline void Addr(const int &x){ for(int i=1;i<=9;i++){ int now=(b[i][0]-b[i][x]+(i==x)); cur+=now; add(b[i][x],now); add(b[0][x],now); add(b[i][0],now); } cur%=P; } inline void Addl(const int &x){ for(int i=1;i<=9;i++){ int now=(b[0][i]-b[x][i]+(i==x)); cur+=now; add(b[x][i],now); add(b[x][0],now); add(b[0][i],now); } cur%=P; } inline void Minusl(const int &x){ for(int i=1;i<=9;i++){ int now=-(b[0][i]-b[x][i]+(i==x)); cur+=now; add(b[x][i],now); add(b[x][0],now); add(b[0][i],now); } cur%=P; } inline void Minusr(const int &x){ for(int i=1;i<=9;i++){ int now=-(b[i][0]-b[i][x]+(i==x)); cur+=now; add(b[i][x],now); add(b[0][x],now); add(b[i][0],now); } cur%=P; } inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int rea(int *a){ char c=nc(); int len=0; for(;c>'z'||c<'a';c=nc());for(;c>='a'&&c<='z';a[++len]=c-'a'+1,c=nc()); return len; } inline void rea(int &x){ char c=nc(); x=0; for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc()); } int main(){ S=sqrt(n=rea(a)); rea(q); for(int i=1;i<=n;i++) pos[i]=i/S; for(int i=1;i<=q;i++) rea(Q[i].l),rea(Q[i].r),Q[i].g=i; sort(Q+1,Q+1+q); int l=1,r=0; for(int i=1;i<=q;i++){ while(r<Q[i].r) Addr(a[++r]); while(r>Q[i].r) Minusr(a[r--]); while(l<Q[i].l) Minusl(a[l++]); while(l>Q[i].l) Addl(a[--l]); ans[Q[i].g]=(cur+P)%P; } for(int i=1;i<=q;i++) printf("%d\n",ans[i]); return 0; }
相关文章推荐
- [倍增NTT][DP] LOJ#6059. 「2017 山东一轮集训 Day1」Sum
- [容斥 DP] LOJ#6077. 「2017 山东一轮集训 Day7」逆序对
- [最短路 杂题] LOJ#6075. 「2017 山东一轮集训 Day6」重建
- [DP][倍增NTT]LOJ#6059. 2017 山东一轮集训 Day1. Sum
- 【LOJ6077】「2017 山东一轮集训 Day7」逆序对 生成函数+组合数+DP
- [后缀自动机 DP] LOJ#6071. 「2017 山东一轮集训 Day5」字符串
- [树的同构][二分][可并堆维护哈希] LOJ#6066 || BZOJ4928 && 2017 山东一轮集训 Day3. 第二题
- LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
- [动态网络 网络流] LOJ#6068.「2017 山东一轮集训 Day4」棋盘
- LOJ.6062.[2017山东一轮集训]Pair(Hall定理 线段树)
- [霍尔定理]「2017 山东一轮集训 Day2」LOJ 6062——PAIR
- [LOJ#6060][线性基]2017 山东一轮集训 Day1. Set
- [费用流]LOJ#6079. 「2017 山东一轮集训 Day7」养猫
- 「长乐集训 2017 Day10」划分序列 (二分 dp)
- LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
- LibreOJ 6#6062. 「2017 山东一轮集训 Day2」Pair
- 【分块+回文自动机】LibreOJ6070(2017 山东一轮集训 Day4)[基因]题解
- [线段树][二分图 霍尔定理]LOJ#6062 && 2017 山东一轮集训 Day2. Pair
- [分块 回文自动机] LOJ#6070. 「2017 山东一轮集训 Day4」基因
- [计数][容斥] LOJ#6065 || BZOJ4927 && 2017 山东一轮集训 Day3. 第一题