「BJOI2020」封印
给出只包含小写字母 , 的两个字符串 , , 次询问,每次询问 和 的最长公共子串长度。
建出 的后缀自动机后在自动机上跑 串得到对于每个 的前缀在 中匹配的最长长度 ,然后对于 ,二分答案 ,检查是否有 即可。
#include<bits/stdc++.h>
#define maxn 400005
#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 lim 18
using namespace std;
char s[maxn],t[maxn];
int q,ls,lt;
int last , fa[maxn] , len[maxn] , tr[maxn][2] , tot;
int st[lim][maxn] , lg[maxn];
void ins(int c){
int u = ++tot , p = last , q;
len[last = u] = len[p] + 1;
for(;p != -1 && !tr[p][c];p=fa[p]) tr[p][c] = u;
if(p == -1) fa[u] = 0;
else if(len[q = tr[p][c]] == len[p] + 1) fa[u] = q;
else{
int v = ++tot;
memcpy(tr[v],tr[q],sizeof tr[q]),fa[v] = fa[q] , len[v] = len[p] + 1;
for(;p != -1 && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = fa[u] = v;
}
}
int qry(int u,int v){
int t = lg[v-u+1];
return max(st[t][u] , st[t][v-(1<<t)+1]);
}
int main(){
scanf("%s%s%d",s,t,&q);
fa[0] = -1;
ls = strlen(s) , lt = strlen(t);
rep(i,0,lt-1) ins(t[i] - 'a');
int u=0,L=0;
rep(i,0,ls-1){
int c = s[i] - 'a';
for(;u!=-1 && tr[u][c]==0;u=fa[u]);
if(u == -1) u = 0 , L = 0;
else L = min(L+1 , len[u] + 1) ,u = tr[u][c];
st[0][i] = L;
}
rep(i,2,ls) lg[i] = lg[i >> 1] + 1;
rep(j,1,lim-1) rep(i,0,ls-(1<<j))
st[j][i] = max(st[j-1][i] , st[j-1][i+(1<<j-1)]);
for(;q--;){
int l,r;scanf("%d%d",&l,&r);l--,r--;
int L = 0 , R = min(r-l+1,lt) , mid;
for(;L<R;){
mid = L+R+1 >> 1;
if(qry(l+mid-1,r) < mid) R = mid - 1;
else L = mid;
}
printf("%d\n",L);
}
}
LOJ #6537. 毒瘤题加强版再加强版
三倍经验
具体来说就是利用异或的性质,所有数异或起来等于出现奇数次的数的异或和。
如果我们要得到单独一个数字,就
一下,具体来说我们只统计
的数的异或和。
那么多用几个
,统计出来出现次数多的数就很有可能是单独一个数字的异或和,反之多个数字的异或和很难在多个不同的
下都一起满足条件。
#include<bits/stdc++.h>
#define vi vector<int>
using namespace std;
int chk(int x){
for(int i=2;i*i<=x;i++) if(x % i == 0) return 0;
return 1;
}
int n,K;
vi mod;
int a[20][20100];
map<int,int>mAp;
int main(){
scanf("%d%d",&n,&K);
for(int i=10000;mod.size() < 20;i++) if(chk(i) && rand() % 2)
mod.push_back(i);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
for(int j=0;j<mod.size();j++)
a[j][x % mod[j]] ^= x;
}
for(int j=0;j<mod.size();j++) for(int i=0;i<mod[j];i++)
if(a[j][i]) mAp[a[j][i]]++;
vector<int>ans;
for(auto v:mAp) if(v.second > 2) ans.push_back(v.first);
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++) printf("%d\n",ans[i]);
}
CF963D Frequency of String
给出
,
次询问
中最短的子串
的长度使得字符串
在
中出现了
次。
所有询问m互不相同。
可以想到求出后缀自动机的
集合
后
回答一次询问。
然后就这样写,然后就可以过。
因为所有询问
互不相同,又因为对于所有长度为
的串他们的
集合大小之和为
。
串不相同代表着只有
种不同的长度,所以所有询问的复杂度为
事实上因为我们只需要
的
集合,所以可以不用写后缀树上启发式合并,直接离线求出所有
的
自动机,拿
在自动机上跑,对于
的前缀所在的节点我们需要更新所有它的后缀的
的
集合,新开一个数组
表示沿着
链跑下一个是
中的一个的节点,因为
集合大小是
,每次暴力爬
也是
的。
来了来了,对于这种屑题,肯定是要写
的啦。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;
char s[maxn],m[maxn];
int n,q,K;
bitset<maxn>C[26];
int main(){
scanf("%s",s);
n = strlen(s);
rep(i,0,n-1) C[s[i] - 'a'][i] = 1;
scanf("%d",&q);
for(;q--;){
scanf("%d%s",&K,m);
int L = strlen(m);
static bitset<maxn>ans;
ans.set();
rep(i,0,L-1) ans &= C[m[i] - 'a'] >> i;
vector<int>a;
for(int i=ans._Find_first();i!=ans.size();i=ans._Find_next(i))
a.push_back(i);
if(a.size() < K) puts("-1");
else{
int ret = 0x3f3f3f3f;
rep(i,K-1,a.size()-1) ret = min(ret , a[i] - a[i-K+1] + L);
printf("%d\n",ret);
}
}
}
CF356E Xenia and String Problem
分类讨论屑题。
串的长度只有可能是
,所以可以计算出所有
串。
改字符,
一.变为
的情况:
1.改中间字符原来在外面出现过现在没出现过,暴力哈希判断即可。
2.改两边字符,原来差一个字符匹配现在不差了,对于所有
串求出往左/右差一个字符的位置贡献上去。
二.变成不是
的情况:
1.改中间字符原来在外面没出现过现在出现过,暴力哈希判断即可。
2.改两边字符,原来就是
串,只要改了两边就一定不是,对于所有
串写区间加即可。
综上,每次就是先减去改字符是中间字符的贡献,再减去(二.2),然后枚举改成那个字符,加上(一.2),加上中间字符的贡献。
真的屑
#include<bits/stdc++.h>
#define maxn 200005
#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--)
using namespace std;
#define lim 17
#define LL long long
#define mod 998244353
#define S 131
char s[maxn];
int f[lim][maxn],n;
int sm[26][maxn];
LL hs[maxn],pw[maxn],p12[maxn][26],p22[maxn],Sm;
LL calc(int u,int v){ return ((hs[v] - hs[u-1] * pw[v-u+1]) % mod + mod) % mod; }
int LCP(int u,int v){
int L = 0 , R = min(n-u+1,n-v+1) , mid;
while(L<R){
mid = (L+R+1) >> 1;
if(calc(u,u+mid-1) == calc(v,v+mid-1))
L = mid;
else
R = mid - 1;
}
return L;
}
int main(){
scanf("%s",s+1);
n = strlen(s+1);
pw[0] = 1;
rep(i,1,n){
f[0][i] = 1;
Sm++;
hs[i] = (hs[i-1] * S + s[i]) % mod;
pw[i] = pw[i-1] * S % mod;
rep(j,0,25)
sm[j][i] = sm[j][i-1] + (s[i] - 'a' == j);
}
rep(j,1,lim-1) rep(i,1,n-(1<<j+1)+2){
if(f[j-1][i] && f[j-1][i+(1<<j)] && sm[s[i+(1<<j)-1] - 'a'][i+(1<<j+1)-2] - sm[s[i+(1<<j)-1] - 'a'][i-1] == 1
&& calc(i,i+(1<<j)-2) == calc(i+(1<<j),i+(1<<j+1)-2))
f[j][i] = 1 , Sm += ((1<<j+1)-1ll) * ((1<<j+1)-1ll) , p22[i] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll) ,
p22[i+(1<<j)-1] -= ((1<<j+1)-1ll) * ((1<<j+1)-1ll) , p22[i+(1<<j)] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll),
p22[i+(1<<j+1)-1] -= ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
else if(f[j-1][i]){
int t = min(LCP(i,i+(1<<j)) , (1<<j)-1);
if(calc(i,i+t-1) == calc(i+(1<<j),i+(1<<j)+t-1)
&& calc(i+t+1,i+(1<<j)-2) == calc(i+(1<<j)+t+1,i+(1<<j+1)-2)
&& sm[s[i+(1<<j)-1] - 'a'][i+(1<<j)-1] - sm[s[i+(1<<j)-1] - 'a'][i-1] == 1)
p12[i+(1<<j)+t][s[i+t]-'a'] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
if(f[j-1][i+(1<<j)] && calc(i,i+t-1) == calc(i+(1<<j),i+(1<<j)+t-1)
&& calc(i+t+1,i+(1<<j)-2) == calc(i+(1<<j)+t+1,i+(1<<j+1)-2)
&& sm[s[i+(1<<j)-1] - 'a'][i+(1<<j+1)-2] - sm[s[i+(1<<j)-1] - 'a'][i+(1<<j)-2] == 1)
p12[i+t][s[i+t+(1<<j)]-'a'] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
}
else if(f[j-1][i+(1<<j)]){
int t = min(LCP(i,i+(1<<j)) , (1<<j)-1);
if(calc(i,i+t-1) == calc(i+(1<<j),i+(1<<j)+t-1)
&& calc(i+t+1,i+(1<<j)-2) == calc(i+(1<<j)+t+1,i+(1<<j+1)-2)
&& sm[s[i+(1<<j)-1] - 'a'][i+(1<<j+1)-2] - sm[s[i+(1<<j)-1] - 'a'][i+(1<<j)-2] == 1)
p12[i+t][s[i+t+(1<<j)]-'a'] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
}
}
rep(i,1,n) p22[i] += p22[i-1];
LL ans = Sm;
rep(i,1,n){
LL t = Sm;
rep(j,1,lim-1) if(i-(1<<j)+1 > 0)
if(f[j][i-(1<<j)+1])
t -= ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
t -= p22[i];
rep(j,0,25) if(j != s[i] - 'a'){
LL ret = t + p12[i][j];
rep(p,1,lim-1) if(i-(1<<p)+1 > 0){
if(f[p-1][i-(1<<p)+1] && f[p-1][i+1] && calc(i-(1<<p)+1,i-1) == calc(i+1,i+(1<<p)-1) &&
sm[j][i-1] - sm[j][i-(1<<p)] == 0)
ret +=((1<<p+1)-1ll) * ((1<<p+1)-1ll);
}
ans = max(ans , ret);
}
}
printf("%lld\n",ans);
}
LOJ #517. 「LibreOJ β Round #2」计算几何瞎暴力
那就看代码吧。
#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
#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 inf 0x3f3f3f3f
using namespace std;
int XOR,qXOR,n,tot,ch[maxn*30][2],last,a[maxn];
struct node{
int a[30],sz;
void ins(int u){ ++sz;rep(i,0,29) if(u>>i&1) a[i]++; }
LL sum(int x=inf){
LL r = 0;
rep(i,0,29) r += (1ll << i) * min(x , XOR>>i&1 ? sz - a[i] : a[i]);
return r;
}
}s[maxn],tr[maxn*30];
void ins(int x){
for(int u=1,p=29;p>=0;tr[u=ch[u][x>>p&1]].ins(x),p--)
if(!ch[u][x>>p&1]) ch[u][x>>p&1]=++tot;
}
LL sum(int x){
if(x > last) return s[x].sum();
int u=1;LL r = 0;
per(i,29,0){
int c=(qXOR>>i&1);
if(x <= tr[ch[u][c]].sz) u =ch[u][c];
else r += tr[ch[u][c]].sum(),x-=tr[ch[u][c]].sz,u=ch[u][!c];
}
return r + tr[u].sum(x);
}
int main(){
scanf("%d",&n);int x;
rep(i,1,n) scanf("%d",&x),a[i]=x,s[i] = s[i-1] , s[i].ins(x);
int Q;scanf("%d",&Q);
tot = 1;
for(int op,l,r;Q--;){
scanf("%d",&op);
if(op == 1) scanf("%d",&x),x^=XOR,s[n+1]=s[n],s[++n].ins(x),a[n]=x;
if(op == 2) scanf("%d%d",&l,&r),printf("%lld\n",sum(r)-sum(l-1));
if(op == 3) scanf("%d",&x),XOR ^= x;
if(op == 4) for(qXOR = XOR;last < n;) ins(a[++last]);
}
}
LOJ #2168. 「POI2011 R3 Day1」周期性 Periodicity
社论
设字符串
的最短周期为
,
的计算可以简单通过
得到。
那么对
分类讨论,
则应该对于一个全为
的字符串。
则应该对应一个前
位全为
,最后一位为
的字符串。
,则将字符串分解为
的形式,递归求
的答案,然后根据最短周期倒推出
所对应的字符串,之所以可以这样做,是因为可以通过有两个周期
并且
那么他们的
也是周期来证明,最短周期是
的情况下长度大于
的所有周期其实都是
的倍数。
因为我们递归求出的答案是一定会满足所有
的周期,所以
是可以满足题意的。
,将字符串分解为
的形式,其中
,递归求
的答案,发现
要么是全为
,要么是只有最后一个为
,简单
判断一下即可。
#include<bits/stdc++.h>
#define maxn 200005
#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--)
using namespace std;
char s[maxn];
int n,nxt[maxn];
void solve(char *s,int n){
for(int j=-1,k=0;k<n;)
if(j == -1 || s[j] == s[k]) nxt[++k] = ++j;
else j = nxt[j];
if(nxt[n] == n-1){
rep(i,0,n-1)s[i] = '0';
return;
}
if(nxt[n] == 0){
rep(i,0,n-2) s[i] = '0';
s[n-1] = '1';
return;
}
int L = n - nxt[n];
if(2 * L <= n){
solve(s+(n/L-1)*L,n%L+L);
per(i,n-L-1,0) s[i] = s[i+L];
}
else{
int l = n % L;
solve(s,l);
rep(i,1,l) s[n-i] = s[l-i];
rep(i,l,n-l-1) s[i] = '0';
for(int j=-1,k=0;k<=n;)
if(j == -1 || s[j] == s[k]) nxt[++k] = ++j;
else j = nxt[j];
if(n - nxt[n] != L) s[n-l-1] = '1';
}
}
int main(){
int T;
scanf("%d",&T);
nxt[0] = -1;
for(;T--;){
scanf("%s",s+1);
n = strlen(s+1);
solve(s+1,n);
puts(s+1);
}
}
#2278. 「HAOI2017」字符串
如果从前往后第一次失配和从后往前第一次失配的位置之差
则匹配,求
在
中出现次数,那么我们建出
的
自动机,对于
的前
个字符,在
自动机上有个位置
,对于
,我们求出
的反串的
自动机,那么这个
反过来进入自动机也有一个位置
。
对于
的前
个字符在正串自动机上有个位置
,后
个字符在反串自动机上也有个位置
。
和
同时删去中间的
个字符后
在
中出现,可以看做在
自动机的后缀树上
在
的子树中,
在
的子树中。
但是可能会有多种删去连续
个字符的方案使得他们相等,会算重,我们发现这些删去的
个字符的所有方案是连续的(比如删去区间是
),所以相邻两个方案(
)对应了一个删去
个字符的方案(
),所以直接减去删去
个字符的方案即可,但是因为
不能对应相邻两个方案,所以不能计算形如这类的贡献。
那么就是两棵树,求在两棵树中点都在当前点对的点的子树内的数量,这可以看做二维平面上的数点问题,但是还有更简单的做法,用天天爱跑步的方法树上差分后即可用
解决一棵树的限制。
#include<bits/stdc++.h>
#define maxn 400005
#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 pb push_back
#define maxc 94
using namespace std;
int K,n,m;
struct node{int id,x,y;node(const int &id=0,const int &x=0,const int &y=0):id(id),x(x),y(y){}};
vector<node>v1[maxn];
vector<int>v2[maxn];
char s[maxn],t[maxn];
int tr[maxn][maxc] , fa[maxn] , tot , pos[maxn] , ps[maxn];
int st[maxn],ed[maxn],tim,ans[maxn];
vector<int>G[maxn];
void dfs(int u){
st[u] = ++tim;
for(int v:G[u]) dfs(v);
ed[u] = tim;
}
int t1[maxn],t2[maxn];
void upd(int u,int v,int *tr){ for(;u<=tot+1;u+=u&-u) tr[u] += v; }
int qry(int u,int *tr){ int r=0;for(;u>0;u-=u&-u) r += tr[u]; return r; }
void dfs2(int u){
for(node t:v1[u]) ans[t.id] -= qry(ed[t.x],t1) - qry(st[t.x]-1,t1) - qry(ed[t.y],t2) + qry(st[t.y]-1,t2);
for(int t:v2[u]) upd(st[pos[t+K+1]],1,t1),upd(st[pos[t+K]],1,t2);
for(int v:G[u]) dfs2(v);
for(node t:v1[u]) ans[t.id] += qry(ed[t.x],t1) - qry(st[t.x]-1,t1) - qry(ed[t.y],t2) + qry(st[t.y]-1,t2);
}
int main(){
scanf("%d%s%d",&K,s+1,&n);
m = strlen(s+1);
rep(i,1,n){
scanf("%s",t+1);
int L = strlen(t+1);
if(L <= K){
ans[i] = m-L+1;
continue;
}
int u = 0;
rep(i,1,L){
int v = t[i]-33;
if(!tr[u][v]) tr[u][v] = ++tot;
u = tr[u][v];
}
ps[L+1] = u = 0;
per(i,L,1){
int v = t[i] - 33;
if(!tr[u][v]) tr[u][v] = ++tot;
u = tr[u][v];
ps[i] = u;
}
for(int j=0,u=0;j+K<=L;u=tr[u][t[j+1]-33],j++)
v1[u].pb(node(i,ps[j+K+1],j?ps[j+K]:maxn-1));
}
static int q[maxn],L=0,R=0;
rep(i,0,93) if(tr[0][i]) q[R++] = tr[0][i];
for(int u;L<R;){
u = q[L++];
rep(i,0,93) if(tr[u][i]) fa[tr[u][i]] = tr[fa[u]][i] , q[R++] = tr[u][i];
else tr[u][i] = tr[fa[u]][i];
}
rep(i,1,tot) G[fa[i]].pb(i);
dfs(0);
for(int i=m,u=0;i>=1;i--) u=tr[u][s[i]-33] , pos[i] = u;
for(int i=0,u=0;i+K<=m;i++) v2[u].pb(i),u=tr[u][s[i+1]-33];
dfs2(0);
rep(i,1,n) printf("%d\n",ans[i]);
}