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号
浙公网安备 33010602011771号