LOJ #6158. A + B Problem
给出一个数字,求在其中两位之间插入一个加号后得到的答案末尾 0 0 0最多的个数。
题解:
第二个数的末尾就是原串的末尾,所以如果需要和第一个数加起来末尾为 0 0 0,那么从后往前扫,第二个数为 0 0 0的位在不进位的情况下第一个数的对应位需要是 0 0 0,不为 0 0 0的位的对应位 x x x应该是 10 − x 10 - x 10−x,然后开启进位模式所有数的对应位都是 9 − x 9-x 9−x,然后求一个原串和转换后的串的最长公共前缀即可得出答案,可以写 e x k m p \rm exkmp exkmp
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 998244353
#define S 131
#define LL long long
using namespace std;
char s[maxn],s0[maxn];
LL hs[2][maxn],pw[maxn];
int n;
LL calc(int a,int b,LL *hs){
return ((hs[b] - hs[a-1] * pw[b-a+1]) % mod + mod) % mod; }
int main(){
pw[0] = 1;
rep(i,1,maxn-1) pw[i] = pw[i-1] * S % mod;
while(scanf("%s",s+1)!=EOF){
n = strlen(s+1);
rep(i,1,n) hs[0][i] = (hs[0][i-1] * S + s[i]) % mod , s0[i] = s[i];
int i;
for(i=n;i>=1 && s[i] == '0';i--);
s[i] = (10 - (s[i] - '0')) + '0';
int t = i;
per(j,i-1,1) s[j] = (9 - (s[j] - '0')) + '0';
rep(i,1,n) hs[1][i] = (hs[1][i-1] * S + s[i]) % mod;
int ans = 0;
rep(i,1,n-1){
int L = 0 , R = min(i , n-i) , mid;
for(;L<R;){
mid = L+R+1 >> 1;
if(calc(i-mid+1,i,hs[0]) == calc(n-mid+1,n,hs[1]))
L = mid;
else
R = mid - 1;
}
if(i <= t){
while(n-L <= i && s0[i-L] == '9') L++;
while(i-L <= 0 && n-L > i && s0[n-L] == '9') L++;
}
else{
while(n-L <= i && s0[i-L] == '0') L++;
while(i-L <= 0 && n-L > i &&s0[n-L] == '0') L++;
}
ans = max(ans , L);
}
printf("%d\n",ans);
}
}
CF914F Substrings in a String
题意:单点修改 S S S中的一个字符,求 S [ l . . . r ] S[l...r] S[l...r]中 T T T的出现次数, ∣ S ∣ , ∑ ∣ T ∣ ≤ 1 e 5 , T i m e l i m i t 6 s |S| , \sum|T| \leq 1e5,\rm Timelimit \ 6s ∣S∣,∑∣T∣≤1e5,Timelimit 6s
太离谱了。
考虑到这是 C F \rm CF CF的 6 s \rm 6s 6s,我们用 b i t s e t C [ i ] [ j ] \rm bitset\ C[i][j] bitset C[i][j]代表字符 i i i在位置 j j j是否出现。
则求出 r e t = and i = 0 ∣ T ∣ − 1 C [ T i ] > > i ret = \operatorname{and}_{i=0}^{|T|-1} C[T_i]>>i ret=andi=0∣T∣−1C[Ti]>>i后取 r e t ret ret在 [ l , r − ∣ T ∣ + 1 ] [l,r-|T|+1] [l,r−∣T∣+1]中的 1 1 1的个数即可。
时间复杂度 O ( n 2 ω ) O(\frac {n^2}{\omega}) O(ωn2)无压力水过
其实可以分块一下得到更科学的复杂度:
对于长度大于块大小 S S S的字符串我们暴力 k m p \rm kmp kmp, O ( n 2 S ) O(\frac {n^2}S) O(Sn2)
对于长度小于块大小 S S S的字符串我们分它是在一个块内还是跨两个块来考虑。
对于块内的答案直接分块求块内的 b i t s e t \rm bitset bitset即可做到 O ( n S ω ) O(\frac {nS}{\omega}) O(ωnS)
对于跨两个块的答案直接对于 n S \frac nS Sn个交界处前后 ∣ T ∣ |T| ∣T∣个字符拿出来跑 k m p \rm kmp kmp, O ( n ∣ T ∣ S ) O(\frac {n|T|}S) O(Sn∣T∣),总复杂度还是 O ( n 2 S ) O(\frac {n^2}S) O(Sn2)
所以 S = ω n S = \sqrt {\omega n} S=ωn时最快,为 O ( n n ω ) O(\frac {n\sqrt n}{\sqrt \omega}) O(ωnn)
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
char s[maxn],ch[maxn];
int Q,n;
bitset<maxn>C[26];
int main(){
scanf("%s",s+1);
n = strlen(s+1);
rep(i,1,n) C[s[i] - 'a'][i] = 1;
scanf("%d",&Q);
for(int op,l,r;Q--;){
scanf("%d%d",&op,&l);
if(op == 1){
scanf("%s",ch);
C[s[l]-'a'][l] = 0;
C[(s[l]=ch[0])-'a'][l] = 1;
}
else{
scanf("%d%s",&r,ch);
int t = strlen(ch);
static bitset<maxn>ans;
ans.set();
rep(i,0,t-1)
ans &= (C[ch[i] - 'a'] >> i);
r -= t - 1;
ans <<= maxn - 1 - r;
ans >>= maxn - 1 - r + l;
printf("%d\n",ans.count());
}
}
}
O ( n n w ) O(\frac {n\sqrt n}{\sqrt w}) O(wnn)做法:(实际上没有快多少)
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define S 1800
using namespace std;
char s[maxn],ch[maxn];
int Q,n,id[maxn],st[maxn],ed[maxn],nxt[maxn],m;
bitset<S>C[26][maxn/S+5];
int kmp(int l,int r){