题面

题目背景

大名鼎鼎的 OI 天花板选手 JZM 对自己的好伙伴——印象笔记有些生疏了

题目描述

作为一名 OI 选手,他的笔记中的字母只包含数字0和1。
JZM 在印象笔记中找到了一行

N

N

N 个字母的印象语录,而 JZM 打算朗读一段长度为

M

M

M 的话语。由于 JZM 记得不是很清楚,他需要从印象语录的某一个位置开始,对照着印象语录进行朗读。当印象语录上的当前字符和 JZM 话语中即将朗读的字符不一样时,JZM 就会跳过这对不一样的字符继续朗读下一个位置,我们称之为发生一次停顿。JZM 所要朗读的话语有些段落文辞优美,有些段落儒雅随和。具体地,该话语中第

i

i

i 个字符有优美度

f

i

f_i

fi​,如果在第

i

i

i 个字符上发生停顿,JZM 的容忍度就会减少

f

i

f_i

fi​ 。JZM 的初始容忍度为

K

K

K,当他的容忍度降至 0 以下时,JZM 就不想继续朗读了。

例如,印象语录为 1101110,而 JZM 则打算从第 3 个字符开始对照,朗读 110011,

K

=

1

K=1

K=1,

f

=

{

1

,

1

,

4

,

5

,

1

,

3

}

f=\{1,1,4,5,1,3\}

f={1,1,4,5,1,3} 。

过程中,印象语录的第三个字符 0 与 JZM 所要朗读的第一字符 1 不同,于是他发生了一次停顿,容忍度减少 1 。

之后,语录中的第四个字符 1 和话语的第二个字符 1 相同,顺利朗读,进入下一对。

印象语录的第五个字符 1 与 JZM 所要朗读的第三个字符 0 不同,于是他又发生了一次停顿,容忍度减少 4,变为 -4,于是 JZM 停下了朗读。

我们把 JZM 停下朗读时当前进行到印象语录中字符的位置称为终止位置,上面这个例子中, JZM 的终止位置为 5 。特别地,假如 JZM 从第

i

i

i 个位置开始顺利朗读完了

M

M

M 个字符,他的终止位置是

i

+

M

i+M

i+M;假如读到一半印象语录结束了,他的终止位置是

N

+

1

N+1

N+1 。

JZM 此时要断章取义,他想尽可能顺利地朗读话语,于是身为 OI 领头羊的他把这个小任务交了你,希望你能告诉他,对于每一个可能的朗读起始位置 1 到 N ,他的终止位置会是哪里。

输入格式

第一行三个整数

N
  

M
  

K

N\;M\;K

NMK 表示印象语录的长度,JZM 朗读话语的长度和 JZM 的容忍度。
第二行

N

N

N 个

0

/

1

0/1

0/1 字符,为印象语录。
第三行

M

M

M 个

0

/

1

0/1

0/1 字符,为 JZM 朗读的话语。
第三行

M

M

M 个整数,为话语中每一个字符的优美度。

输出格式

输出一行

N

N

N 个整数,第

i

i

i 个整数表示对于朗读起始位置

i

i

i ,JZM 的终止位置会是哪里。

Sample Input

20 10 15
10011111001101010110
1011110101
2 1 4 7 4 8 3 6 4 7

Sample Output

11 12 13 13 11 13 17 15 19 15 20 18 20 21 21 21 21 21 21 21

数据范围

对于测试点1:

N

,

M

≤

1

0

3

N,M \leq 10^3

N,M≤103
对于测试点2~3:

K

=

0

K=0

K=0
对于测试点4 :

K

≤

100

K\leq 100

K≤100
对于测试点5~6:话语中所有字符均为 1
对于测试点7~10 : 无特殊限制
对于所有的数据,

1

≤

N

,

M

≤

1

0

5

  

,
  

0

≤

K

≤

1

0

8

  

,
  

1

≤

f

i

≤

1

0

3

1\leq N,M\leq 10^5\;,\;0\leq K\leq 10^8\;,\;1\leq f_i\leq10^3

1≤N,M≤105,0≤K≤108,1≤fi​≤103
时间限制 2 s,空间限制 64 MB。

题解

考试中,我正敲着 K ≤ 100 的部分分,调试着后缀数组+ RMQ 的百行大代码,忽然听见对面的大佬们在小声讨论:

        

\;\;\;\;

“你用的是 FFT 还是 NTT ? 我觉得 FFT 可能有点慢。”
NTT ? 哪一到题?T1 跟 NTT 有什么关系?

        

\;\;\;\;

“你开的块大小是多少?我开了 3000”
分块?・∀・?怎么又跟分块扯上关系了?
罢了罢了,肯定不是这道题。
于是我就打了前面四个点的暴力,相比起他们的 AC,我只拿了 50 pts(多了十分?看下文)

如果限定终止位置在哪里,我们就可以想想怎么快速地算容忍度会降多少,是否降到 0 以下。
设话语为 S 字符串 ,印象语录为 T 字符串 ,

a

n

s

i

ans_i

ansi​ 代表第

i

i

i 个位置开始匹配完整个字符串 S 后消耗的容忍度,则:

a

n

s

i

=

∑

j

=

1

m

(

S

[

i

+

j

−

1

]
 

x

o

r
 

T

[

j

]

)

⋅

f

j

ans_i=\sum_{j=1}^{m}(S[i+j-1]\,xor\,T[j])\cdot f_j

ansi​=j=1∑m​(S[i+j−1]xorT[j])⋅fj​

这是笔者进行的基础的题面翻译,大佬们是怎么跟 NTT 扯上关系的呢?

我们想,异或的本质是同 0 异 1,互异的情况只有 0,1 和 1,0 (这不废话)

于是考虑这两种情况算贡献,顺便把

i

+

j

−

1

i+j-1

i+j−1 变成

k
  

,

(

k

−

j

=

i

−

1

)

k\;,(k-j=i-1)

k,(k−j=i−1):

a

n

s

i

=

∑

k

−

j

=

i

−

1

f

j

(

[

S

k

=

1

]

[

T

j

=

0

]

+

[

S

k

=

0

]

[

T

j

=

1

]

)

ans_i=\sum_{k-j=i-1}f_j([S_k=1][T_j=0] + [S_k=0][T_j=1])

ansi​=k−j=i−1∑​fj​([Sk​=1][Tj​=0]+[Sk​=0][Tj​=1])

其中的

[

S

k

=

1

]

[S_k=1]

[Sk​=1] 是个条件表达式,

S

k

=

1

S_k=1

Sk​=1 则值为 1,否则为 0,或许这样还是不太明显,我们再变:

a

n

s

i

=

∑

k

−

j

=

i

−

1

[

S

k

=

1

]

⋅

(

f

j

[

T

j

=

0

]

)

+

∑

k

−

j

=

i

−

1

[

S

k

=

0

]

⋅

(

f

j

[

T

j

=

1

]

)

ans_i=\sum_{k-j=i-1}[S_k=1]\cdot (f_j[T_j=0]) + \sum_{k-j=i-1}[S_k=0]\cdot (f_j[T_j=1])

ansi​=k−j=i−1∑​[Sk​=1]⋅(fj​[Tj​=0])+k−j=i−1∑​[Sk​=0]⋅(fj​[Tj​=1])

两个卷积!(只需把数组倒一倒)这该是多么强的直觉才能发现是卷积啊

所以我们就可以在

(

N

+

M

)

log

⁡

N

(N+M)\log N

(N+M)logN 的时间求出从每个位置开始、匹配完整条话语的容忍度下降值。

那怎么想到分块的呢?

同样地,我们可以在

(

N

+

B

)

log

⁡

N

(N+B)\log N

(N+B)logN 的时间求出从每个位置开始、匹配完话语中一段长为

B

B

B 的块的容忍度下降值。

那么考虑将 S 分

M

B

\frac{M}{B}

BM​ 个为大小为

B

B

B 的块,从前到后 NTT 计算每个块的卷积,假如某个位置已经不能再匹配完这个块(容忍度在这个块降到 0 以下),则

O

(

B

)

O(B)

O(B) 地算出它在哪里终止。

大概复杂度在

O

(

N

⋅

M

B

log

⁡

N

+

N

⋅

B

)

O(N\cdot\frac{M}{B}\log N+N\cdot B)

O(N⋅BM​logN+N⋅B) 左右,因此理论上

B

=

N

log

⁡

N

B=\sqrt{N\log N}

B=NlogN

​ 最优,考虑暴力常数小,我们就取

B

=

m

a

x

(

M

30

,

1

)

B=max(\frac{M}{30},1)

B=max(30M​,1) 就行了,最后还得卡卡常。

CODE(正解)

#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k,B;
int f[MAXN];
char ss[MAXN],s1[MAXN],s2[MAXN<<1];
int QM(int x) {return x>=MOD ? (x-MOD) : x;}
int qkpow(int a,int b) {
int res = 1;
while(b > 0) {
if(b & 1) res = res *1ll* a % MOD;
a = a *1ll* a % MOD;b >>= 1;
}return res;
}
int xm[MAXN<<2],rev[MAXN<<2],om;
void NTT(int *s,int n,int op) {
for(int i = 1;i < n;i ++) {
rev[i] = ((rev[i>>1]>>1) | ((i & 1) ? (n>>1):0));
if(rev[i] < i) swap(s[rev[i]],s[i]);
}
om = qkpow(3,(MOD-1)/n); xm[0] = 1;
if(op < 0) om = qkpow(om,MOD-2);
for(int i = 1;i <= n;i ++) xm[i] = xm[i-1] *1ll* om % MOD;
for(int k = 2,t=(n>>1);k <= n;k<<=1,t>>=1) {
for(int j = 0;j < n;j += k) {
for(int i = j,l = 0;i < j+(k>>1);i ++,l += t) {
int A = s[i],B = s[i+(k>>1)];
s[i] = (A + B *1ll* xm[l] % MOD) % MOD;
s[i+(k>>1)] = (A +MOD- B *1ll* xm[l] % MOD) % MOD;
}
}
}
if(op < 0) {
int invn = qkpow(n,MOD-2);
for(int i = 0;i < n;i ++) s[i] = s[i] *1ll* invn % MOD;
}return ;
}
int kk[MAXN],rl[MAXN];
int aa[MAXN<<2],bb[MAXN<<2];
int C[MAXN<<2],D[MAXN<<2];
int rpos(int pos,int st,int kk) {
for(int j = st;j <= m && pos <= n;j ++) {
if(ss[pos] != s1[j]) kk -= f[j];
if(kk < 0) break;
pos ++;
}
return pos;
}
int main() {
freopen("mata.in","r",stdin);
freopen("mata.out","w",stdout);
n = read(); m = read(); k = read();
B = max(1,m/30);
scanf("%s",ss + 1);
scanf("%s",s1 + 1);
LL sm = 0;
for(int i = 1;i <= m;i ++) f[i] = read(),sm += f[i];
if(k >= sm) {
for(int i = 1;i <= n;i ++) {
printf("%d ",min(n+1,i+m));
}ENDL;
return 0;
}
int le = 1; while(le <= n+B) le <<= 1;
for(int i = 1;i <= n;i ++) {
kk[i] = k;
aa[i] = (ss[n-i+1] == '1' ? 1:0);
bb[i] = aa[i] ^ 1;
}
NTT(aa,le,1); NTT(bb,le,1);
for(int i = 1;i <= m;i += B) {
int rr = min(i+B-1,m);
for(int j = 0;j <= le;j ++) C[j]=D[j]=0;
for(int j = 1,j2 = i;j2 <= rr;j2 ++,j ++) {
C[j] = (s1[j2] == '1' ? f[j2]:0);
D[j] = f[j2] - C[j];
}
NTT(C,le,1);NTT(D,le,1);
for(int j = 0;j < le;j ++) C[j] = C[j] *1ll* bb[j] % MOD,D[j] = D[j] *1ll* aa[j] % MOD;
NTT(C,le,-1);NTT(D,le,-1);
for(int j = 1;j <= n+1-i;j ++) {
int ad = n-j+3-i;
if(!rl[j]) {
if(kk[j] - C[ad] - D[ad] < 0) {
rl[j] = rpos(j+i-1,i,kk[j]);
}
kk[j] -= C[ad]+D[ad];
}
}
}
for(int i = 1;i <= n;i ++) {
if(kk[i] >= 0) rl[i] = min(n+1,i+m);
printf("%d ",rl[i]);
}ENDL;
return 0;
}

彩蛋

为什么我会得 50 pts 呢?我看了下结果,有 40 的有 60 的,就我和一个正解打爆的人得了 50 。

我过了最后一个点,0.2 s,正解跑 1.5 s。

这是因为我特判了一种情况:当

K

≥

∑

f

i

K\geq \sum f_i

K≥∑fi​ 时,每个位置都可以随便匹配,因此对于位置

i

i

i 只用输出

min

⁡

(

i

+

M

,

N

+

1

)

\min(i+M,N+1)

min(i+M,N+1) ,如果是暴力的话,这个点可以卡掉那些认为 K 很小可以中途退出的人,也可以卡掉常数不是很优的正解,关键是可以卡掉边界处理不好的人。

但我把它特判了 。

@偶耶XJX !快更新《出题人心理学》!有新点子!

50 pts 代码(可以拍拍后缀数组+rmq):

#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
int f[MAXN];
char ss[MAXN],s1[MAXN],s2[MAXN<<1];
int sa[MAXN<<1],rk[MAXN<<1],ht[MAXN<<1],h[MAXN<<1];
int nx[MAXN<<1],hd[MAXN<<1],tl[MAXN<<1],pr[MAXN<<2];
int ins(int i,int x) {return hd[i]==0 ? (hd[i]=x):(nx[tl[i]]=x);}
void INIT_SA(char *s,int *sa,int *rk,int n) {
for(int i=1;i<=n;i++)sa[i]=rk[i]=nx[i]=hd[i]=tl[i]=pr[i+n]=0;
for(int i = 1;i <= n;i ++) {
nx[tl[(int)s[i]] = ins((int)s[i],i)] = 0;
}
int cn = 0,nm = 0;
for(int i = 0;i <= 256;i ++) {
int p = hd[i];
if(p) nm ++;
while(p) {
sa[++ cn] = p;
rk[p] = nm;
if(p == tl[i]) break;
p = nx[p];
} hd[i] = tl[i] = 0;
}
for(int ii = 1;ii < n;ii <<= 1) {
for(int i = 1;i <= n;i ++) pr[i] = rk[i],rk[i]=0;
for(int i = n-ii+1;i <= n;i ++) {
nx[tl[pr[i]] = ins(pr[i],i)] = 0;
}
for(int i = 1;i <= n;i ++) {
if(sa[i] <= ii) continue;
nx[tl[pr[sa[i]-ii]] = ins(pr[sa[i]-ii],sa[i]-ii)] = 0;
}
int cn = 0,nm = 0;
for(int i = 1;i <= n;i ++) {
int p = hd[i],pp = 0;
while(p) {
sa[++ cn] = p;
rk[p] = (!pp || pr[p+ii] != pr[pp+ii] ? (++nm):nm);
if(p == tl[i]) break;
pp = p; p = nx[p];
} hd[i] = tl[i] = 0;
}
}return ;
}
void INIT_HEIGHT(char *s,int *sa,int *rk,int *hi,int n) {
hi[0] = 0; sa[0] = 0;
for(int i = 1;i <= n;i ++) {
int k = sa[rk[i]-1];if(!k) {hi[i]=0;continue;}
hi[i] = max(0,hi[i-1]-1);
while(s[i+hi[i]] == s[k+hi[i]]) hi[i] ++;
}return ;
}
int dp[MAXN<<1][20]; // 18
int hb[MAXN<<1];
int lcp(int l,int r) {
if(l > r) swap(l,r);
if(l == r) return n+m;
int le = hb[r-l];
return min(dp[l+1][le],dp[r-(1<<le)+1][le]);
}
int main() {
freopen("mata.in","r",stdin);
freopen("mata.out","w",stdout);
n = read(); m = read(); k = read();
scanf("%s",ss + 1);
scanf("%s",s1 + 1);
LL sm = 0;
for(int i = 1;i <= m;i ++) f[i] = read(),sm += f[i];
bool flag1 = 1;
for(int i = 1;i <= n;i ++) if(ss[i] != '1') flag1 = 0;
for(int i = 1;i <= m;i ++) if(s1[i] != '1') flag1 = 0;
if(n <= 1000 && m <= 1000) {
for(int i = 1;i <= n;i ++) {
int kk = k,pos = i;
for(int j = 1;j <= m && pos <= n;j ++) {
if(ss[pos] != s1[j]) kk -= f[j];
if(kk < 0) break;
pos ++;
}
printf("%d ",pos);
}ENDL;
return 0;
}
if(flag1 || k >= sm) {
for(int i = 1;i <= n;i ++) {
printf("%d ",min(n+1,i+m));
}ENDL;
return 0;
}
if(k <= 100) {
for(int i = 1;i <= n;i ++) s2[i] = ss[i];
for(int i = 1;i <= m;i ++) s2[n+i+1] = s1[i];
s2[n+1] = '2'; s2[n+m+2] = '3';
int nle = n+m+1; hb[1] = 0;
for(int i = 2;i <= nle;i ++) hb[i] = hb[i>>1]+1;
INIT_SA(s2,sa,rk,nle);
INIT_HEIGHT(s2,sa,rk,ht,nle);
for(int i = 1;i <= nle;i ++) dp[i][0] = h[i] = ht[sa[i]];
for(int i = nle;i > 0;i --) {
for(int j = 1;j <= 18 && i+(1<<j)-1<=nle;j ++) {
dp[i][j] = min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
for(int i = 1;i <= n;i ++) {
int ad = i-1,le = 0,la = 0;
int st = 0,kk = k;
while(la <= m && i-1+la <= n && kk >= 0) {
le = lcp(rk[ad+1],rk[n+st+2]);
ad += le+1; la += le+1;
st += le+1; kk -= f[la];
}
printf("%d ",i+la-1);
}ENDL;
return 0;
}
return 0;
}

JZM 的印象笔记 (卷积,分块)的更多相关文章

  1. [Tensorflow] Cookbook - Retraining Existing CNNs models - Inception Model

    From: https://github.com/jcjohnson/cnn-benchmarks#alexnet 先大概了解模型,再看如果加载pre-training weight. 关于retai ...

  2. Inception in CNN

    之前也写过GoogLeNet的笔记.但那个时候对Inception有些似懂非懂,这周又一次看了一遍,觉得有了新的体会,特地又一次写一篇博客与它再续前缘. 本文属于论文笔记性质.特此声明. Networ ...

  3. 从零搭建Pytorch模型教程(三)搭建Transformer网络

    ​ 前言 本文介绍了Transformer的基本流程,分块的两种实现方式,Position Emebdding的几种实现方式,Encoder的实现方式,最后分类的两种方式,以及最重要的数据格式的介绍. ...

  4. CC countari & 分块+FFT

    题意: 求一个序列中顺序的长度为3的等差数列. SOL: 对于这种计数问题都是用个数的卷积来进行统计.然而对于这个题有顺序的限制,不好直接统计,于是竟然可以分块?惊为天人... 考虑分块以后的序列: ...

  5. CC Arithmetic Progressions (FFT + 分块处理)

    转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 题目:给出n个数,选出三个数,按下标顺序形成等差数 ...

  6. 卷积神经网络的变种: PCANet

    前言:昨天和大家聊了聊卷积神经网络,今天给大家带来一篇论文:pca+cnn=pcanet.现在就让我带领大家来了解这篇文章吧. 论文:PCANet:A Simple Deep Learning Bas ...

  7. SSE图像算法优化系列十一:使用FFT变换实现图像卷积。

    本文重点主要不在于FFT的SSE优化,而在于使用FFT实现快速卷积的相关技巧和过程. 关于FFT变换,有很多参考的代码,特别是对于长度为2的整数次幂的序列,实现起来也是非常简易的,而对于非2次幂的序列 ...

  8. 狄利克雷卷积&莫比乌斯反演总结

    狄利克雷卷积&莫比乌斯反演总结 Prepare 1.\([P]\)表示当\(P\)为真时\([P]\)为\(1\),否则为\(0\). 2.\(a|b\)指\(b\)被\(a\)整除. 3.一 ...

  9. 优化基于FPGA的深度卷积神经网络的加速器设计

    英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...

随机推荐

  1. GDKOI 2021 Day3 PJ 懵逼记

    今天早了一点起来,初三的大奆都来做比赛了, 自然,自测的有许多 AK 虽然今天的题比昨天简单,但还是脑子还是十分迟钝,以至于贪心都想不出 真为明天的提高组而担忧 T1 斜率被卡 90 ,直接用勾股定理 ...

  2. mysql调优学习笔记

    性能监控 使用show profile查询剖析工具,可以指定具体的type 此工具默认是禁用的,可以通过服务器变量在绘画级别动态的修改 set profiling=1; 当设置完成之后,在服务器上执行 ...

  3. Redis 中的事务分析,Redis 中的事务可以满足ACID属性吗?

    Redis 中的事务 什么是事务 1.原子性(Atomicity) 2.一致性(Consistency) 3.隔离性(Isolation) 4.持久性(Durability) 分析下 Redis 中的 ...

  4. 写了个基于 MacOS + iTerm2 自动打开窗口执行命令的工具

    大家好,我是秋风,今天要给大家带来的这个工具是我最近写的 一个 npm 工具.mmt 是基于 MacOS + iTerm2 ,目的主要是为了提高日常生活中的效率,接下来我带大家看看一些常用的一些场景. ...

  5. Maven的安装 和idea的配置

    Maven的安装 和idea的配置 工欲善其事 必先利其器 1 下载maven 官网 下滑 找到Files tar.gz 是linux系统的 .zip window系统 2 maven安装和配置到环境 ...

  6. SAP IDOC

    物料主数据 供应商主数据 价格档案 采购订单 采购计划协议 srm发货单 物料凭证 发票校验 发票校验过账或删除信息返回 CALL FUNCTION 'BAPI_INCOMINGINVOICE_PAR ...

  7. Contest

    Contest 题目 链接 题目描述 \(n\) 支队伍一共参加了三场比赛. 一支队伍 \(x\) 认为自己比另一支队伍 \(y\) 强当且仅当 \(x\) 在至少一场比赛中比 \(y\) 的排名高. ...

  8. NC224938 加减

    NC224938 加减 题目 题目描述 小红拿到了一个长度为 \(n\) 的数组.她每次操作可以让某个数加 \(1\) 或者某个数减 \(1\) . 小红最多能进行 \(k\) 次操作.她希望操作结束 ...

  9. go语言学习笔记-初识Go语言

    Go语言是怎样诞生的? Go语言的创始人有三位,分别是图灵奖获得者.C语法联合发明人.Unix之父肯·汤普森(Ken Thompson).Plan 9操作系统领导者.UTF-8编码的最初设计者罗伯·派 ...

  10. 【cartogarpher_ros】三: 发布和订阅雷达scan信息

    上一节介绍和测试了cartographer的官方demo. 本节会编写ros系统中,最常用的激光雷达LaserScan传感数据的订阅和发布,方便在cartographer中加入自己的数据进行建图与定位 ...