HZNU训练补题记录
题意:给你一个字符串,问 最长循环串/循环节 最大为多少
解析:实际上就是求 i 位到末尾与 j 位到末尾的字符串的最长公共前缀除以 j-i+1
我们可以将原串翻转,lcp实际上就是两个节点的LCA,实际上即是某点的所有endpos,在一个endpos集中明显相邻最小,贡献为 某点长度/(j-i+1)
利用set维护endpos集合转移取min即可,注意set维护endpos集合的写法
#include <bits/stdc++.h>
const int maxn=4e5+50;
using namespace std;
typedef long long ll;
int head[maxn],nex[maxn],to[maxn],ecnt;
int size[maxn];
set<int>st[maxn];
string s;
int ord[maxn];
int pr[maxn];
int mi[maxn];
const int inf=0x3f3f3f3f;
struct node {
int ch[26];
int len;
int fa;
} a[maxn<<1];
int pre=1,tot=1;
inline void add(int x,int y) {
to[++ecnt]=y;
nex[ecnt]=head[x];
head[x]=ecnt;
}
inline void init() {
pre=tot=1;
for(int i=0; i<26; i++) {
a[1].ch[i]=0;
}
a[1].len=0;
a[1].fa=0;
}
inline int newnode() {
++tot;
a[tot].len=0;
a[tot].fa=0;
for(int i=0; i<26; i++) {
a[tot].ch[i]=0;
}
return tot;
}
inline int insert(int c) {
int p=pre,np=pre=newnode();
size[tot]=1;
a[np].len=a[p].len+1;
for(; p&&!a[p].ch[c]; p=a[p].fa) a[p].ch[c]=np;
if(!p) a[np].fa=1;
else {
int q=a[p].ch[c];
if(a[q].len==a[p].len+1) a[np].fa=q;
else {
int nq=newnode();
a[nq]=a[q];
a[nq].len=a[p].len+1;
a[q].fa=a[np].fa=nq;
for(; p&&a[p].ch[c]==q; p=a[p].fa) a[p].ch[c]=nq;
}
}
return pre;
}
inline int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
signed main() {
ios::sync_with_stdio(false);
cin>>s;
reverse(s.begin(),s.end());
int len=s.length();
for(int i=0; i<len; i++) {
int p=insert(s[i]-'a');
st[p].insert(i+1);
}
int pos=1;
for(int i=0; i<len; i++) {
pos=a[pos].ch[s[i]-'a'];
pr[i+1]=pos;
}
for(int i=2; i<=tot; i++) {
add(a[i].fa,i);
ord[i]=i;
mi[i]=inf;
}
mi[1]=inf;
ord[1]=1;
sort(ord+1,ord+1+tot,[](int x,int y) {
return a[x].len>a[y].len;
});
int f=0,fz=0,fm=1,cnt=0;
for(int k=1; k<=tot; k++) {
int x=ord[k];
if(x==1)continue;
for(int i=head[x]; i; i=nex[i]) {
int y=to[i];
mi[x]=min(mi[x],mi[y]);
if(st[y].empty())continue;
if(st[x].empty()) {
swap(st[x],st[y]);
continue;
}
auto &L=st[x],&R=st[y];
if(L.size()<R.size())swap(L,R);
for(auto it:R) {
set<int>::iterator it1;
set<int>::iterator it2;
it1=st[x].upper_bound(it);
if(it1==st[x].begin()) {
mi[x]=min(mi[x],*it1-it);
} else if(it1==st[x].end()&&st[x].size()) {
mi[x]=min(mi[x],it-*(st[x].begin()));
} else if(st[x].size()) {
it2=it1;
it1--;
mi[x]=min(mi[x],it-*it1);
mi[x]=min(mi[x],*it2-it);
}
st[x].insert(it);
}
if(st[x].size()<2)continue;
int fx=a[x].len;
int fy=mi[x];
if(!f&&fx&&fy) {
fz=fx;
fm=fy;
f=1;
} else {
if(1ll*fz*fy<1ll*fm*fx) {
fz=fx;
fm=fy;
}
}
}
}
fz=fz+fm;
int gz=gcd(fz,fm);
fz/=gz;
fm/=gz;
cout<<fz<<"/"<<fm<<'\n';
}
题意:给出一个长度为n的串A,B为A的循环同构串,给出一个字符串数量为m的字符串集合,求B中长度为n的子串中与串集的最长公共字串最小的最长公共子串长度
解析:对集合串构建广义后缀自动机,用B串在后缀机上跑,就能跑出最长公共子串的集合,再利用这个集合去统计答案,pre与suf统计的是在边界的最长公共子串贡献,ss统计在内部的最长公共子串贡献,最后对每个位置取min即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;
const int INF=0x3f3f3f3f;
int n,m,tot,last,len[maxn],fa[maxn],ch[maxn][26];
int cnt[maxn];
int Ins(int c,int last) {
int p=last;
if(ch[p][c]) {
int q=ch[p][c];
if(len[p]+1==len[q]) {
return q;
} else {
int nq=++tot;
len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];
fa[q]=nq;
for(; p&&ch[p][c]==q; p=fa[p])ch[p][c]=nq;
return nq;
}
}
int np=++tot;
len[np]=len[p]+1;
for(; p&&!ch[p][c]; p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else {
int q=ch[p][c];
if(len[p]+1==len[q])fa[np]=q;
else {
int nq=++tot;
len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for(; p&&ch[p][c]==q; p=fa[p])ch[p][c]=nq;
}
}
return np;
}
set<pair<int,int>>st;
int sum[maxn];
string s,t;
set<pair<int,int>>ss;
vector<int> q;
vector<pair<int,int>> v;
vector<pair<pair<int,int>,int>>u;
vector<pair<pair<int,int>,int>>w;
int suf[maxn],ans[maxn],pre[maxn];
int main() {
ios::sync_with_stdio(false);
cin>>n>>m;
int lns=2*n;
cin>>s;
s=s+s;
tot=1;
while(m--) {
last=1;
cin>>t;
int ln=t.length();
for(int i=0; i<ln; i++) {
last=Ins(t[i]-'a',last);
}
}
int length=0,pos=1;
for(int i=0; i<lns; i++) {
if(ch[pos][s[i]-'a']) {
length++;
pos=ch[pos][s[i]-'a'];
if(i==2*n-1) {
if(length!=0) {
v.push_back({i+1-length+1,i+1});
}
}
} else {
if(length!=0) {
v.push_back({i-length+1,i});
}
while(pos&&!ch[pos][s[i]-'a'])pos=fa[pos];
if(pos==0) {
pos=1;
length=0;
} else {
length=len[pos]+1;
pos=ch[pos][s[i]-'a'];
}
}
}
for (int i = 0; i<=lns+1; i++) {
suf[i] = INF;
pre[i] = -1;
}
for(int i=0; i<v.size(); i++) {
pre[v[i].first]=max(pre[v[i].first],v[i].second);
suf[v[i].second]=min(suf[v[i].second],v[i].first);
}
for(int i=lns; i>=1; i--) {
suf[i]=min(suf[i],suf[i+1]);
}
for(int i=1; i<=lns; i++) {
pre[i]=max(pre[i],pre[i-1]);
}
for(int i=1; i<=n+1; i++) {
int j=i+n-1;
ans[i]=max(ans[i],min(j,pre[i])-i+1);
ans[i]=max(ans[i],j-max(i,suf[j])+1);
}
for(int i=0; i<v.size(); i++) {
if(v[i].second-v[i].first+1<=n) {
u.push_back({{v[i].second, v[i].first}, i});
w.push_back({{v[i].first, v[i].second}, i});
}
}
sort(w.begin(), w.end());
int k=0,l=0;
for (int i = 1; i <= n; i++) {
int j = i + n - 1;
while (k < u.size() && u[k].first.first <= j) {
ss.insert(make_pair(u[k].first.first - u[k].first.second + 1, u[k].second));
k++;
}
while (l < w.size() && w[l].first.first < i) {
ss.erase(make_pair(w[l].first.second - w[l].first.first + 1, w[l].second));
l++;
}
if (ss.size() != 0) {
auto it = *(--ss.end());
ans[i] = max(ans[i], it.first);
}
}
cout<<*min_element(ans+1, ans + 1+n)<<'\n';
}
题意:
给你n个空位 m个数据集 和q个查询 一开始所有空位数据都是0,每个数据集都有一些数,接下来有3种操作
1 i p 就是把第i个数据集覆盖再从p开始的空位上 ,保证位置合法
2 p 把第p个位置的数据打出来
3 i l r 把第i个数据集 从l位置到r位置的所有数据+1 (之后要mod256)
解析:
离线操作,将数据集合整合成一个树,自己的又是一棵树,每次1操作将区间赋值l~r,并且记录一个时间点,2操作记录该次查询的点与时间点,然后离线时查询根据时间点排序,离线时用3操作更新再利用时间点查询即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int a[maxn];
struct node {
int l,r;
int sum;
int id;
} tree[maxn<<2],tr[maxn<<2];
int lazy[maxn<<2],laz[maxn<<2],lazs[maxn<<2];
inline void pushdown(int i) {
if(lazy[i]) {
lazy[i<<1]+=lazy[i];
lazy[i<<1|1]+=lazy[i];
tree[i<<1].sum+=lazy[i];
tree[i<<1|1].sum+=lazy[i];
lazy[i]=0;
}
}
inline void build(int i,int l,int r) {
tree[i].l=l;
tree[i].r=r;
if(l==r) {
tree[i].sum=a[l];
return;
}
int mid=l+r>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
}
inline void update(int i,int l,int r,int val) {
if(l<=tree[i].l&&tree[i].r<=r) {
tree[i].sum+=val;
lazy[i]+=val;
return;
}
int mid=tree[i].l+tree[i].r>>1;
if(l<=mid)update(i<<1,l,r,val);
if(r>mid)update(i<<1|1,l,r,val);
}
inline int query(int i,int pos) {
if(tree[i].l==tree[i].r) {
return tree[i].sum;
}
pushdown(i);
int mid=tree[i].l+tree[i].r>>1;
if(pos<=mid)return query(i<<1,pos);
else return query(i<<1|1,pos);
}
inline void pud(int i) {
if(laz[i]) {
laz[i<<1]=laz[i];
laz[i<<1|1]=laz[i]+(tr[i<<1].r-tr[i<<1].l+1);
tr[i<<1].sum=laz[i<<1];
tr[i<<1|1].sum=laz[i<<1|1];
laz[i]=0;
}
if(lazs[i]) {
lazs[i<<1]=lazs[i];
lazs[i<<1|1]=lazs[i];
tr[i<<1].id=lazs[i<<1];
tr[i<<1|1].id=lazs[i<<1|1];
lazs[i]=0;
}
}
inline void builds(int i,int l,int r) {
tr[i].l=l;
tr[i].r=r;
tr[i].sum=0;
if(l==r)return;
int mid=l+r>>1;
builds(i<<1,l,mid);
builds(i<<1|1,mid+1,r);
}
inline void up(int i,int l,int r,int st,int val,int k) {
if(l<=tr[i].l&&tr[i].r<=r) {
tr[i].sum=val+(tr[i].l-st);
laz[i]=val+(tr[i].l-st);
tr[i].id=k;
lazs[i]=k;
return;
}
pud(i);
int mid=tr[i].l+tr[i].r>>1;
if(l<=mid)up(i<<1,l,r,st,val,k);
if(r>mid)up(i<<1|1,l,r,st,val,k);
}
struct pis {
int pos,val,id;
} re[maxn];
int rcnt;
int res[maxn];
inline pis qu(int i,int pos) {
if(tr[i].l==tr[i].r) return {tr[i].id,tr[i].sum};
pud(i);
int mid=tr[i].l+tr[i].r>>1;
if(pos<=mid)return qu(i<<1,pos);
else return qu(i<<1|1,pos);
}
int cmp(pis a,pis b) {
return a.pos<b.pos;
}
struct point {
int op,l,r,x;
} e[maxn],p[maxn];
int len[maxn];
int main() {
ios::sync_with_stdio(false);
int n,m,q;
int tot=0;
cin>>n>>m>>q;
for(int i=1; i<=m; i++) {
int k;
cin>>k;
p[i].l=tot+1;
p[i].r=tot+1+k-1;
len[i]=k;
for(int j=1; j<=k; j++) {
cin>>a[++tot];
}
}
build(1,1,tot);
builds(1,1,n);
for(int i=1; i<=q; i++) {
cin>>e[i].op;
if(e[i].op==1) {
cin>>e[i].l>>e[i].r;
up(1,e[i].r,e[i].r+len[e[i].l]-1,e[i].r,p[e[i].l].l,i);
} else if(e[i].op==2) {
cin>>e[i].l;
pis now=qu(1,e[i].l);
re[++rcnt]= {now.pos,now.val,rcnt};
} else {
cin>>e[i].x>>e[i].l>>e[i].r;
}
}
sort(re+1,re+1+rcnt,cmp);
int kk=1;
for(int i=1; i<=q; i++) {
if(e[i].op==3) {
update(1,p[e[i].x].l+e[i].l-1,p[e[i].x].l+e[i].r-1,1);
}
while(re[kk].pos<=i&&kk<=rcnt) {
if(re[kk].pos==0) {
res[re[kk].id]=0;
} else {
res[re[kk].id]=query(1,re[kk].val);
}
kk++;
}
}
for(int i=1; i<=rcnt; i++) {
cout<<res[i]%256<<'\n';
}
}

浙公网安备 33010602011771号