题解 肃正协议

传送门
CF1063F

考场思路是将fail树加入sam形成的DAG中,这样一个串是另一个串的条件是可达另一个串
但复杂度是 \(O(n^2\sqrt n)\)

发现首先可以钦定选中的最短的串长度为1,且所有选中的串长度恰好相差1
于是考虑DP,令 \(dp_i\) 为恰好以点 \(i\) 为起点时选的串数的最大值(同时也是以 \(i\) 为起点的那个串的长度)
发现可以二分
但其实可以发现 \(dp_i\leqslant dp_{i+1}+1\)

  • 注意找形如「相邻的DP值相差不超过1」的限制,有时可以去掉二分的log

于是每个 \(dp_i\)\(dp_{i+1}+1\) 开始向下爆扫即可(下标每减少1最多增加1的势能)

考虑怎么check一个 \(dp_i\) 是否合法
有一个 \(O(n\sqrt n)\) 的暴力做法是依据有用的区间长度不超过根号,所以可以预处理所有合法区间的hash值
然后每次就要在 \([i+dp_i, n]\) 中查询一个DP值是否出现过
发现这个 \(i+dp_i\) 单调不升,所以对这个东西维护一个指针即可

然后考虑优化
发现我们实际上是要找到一个点 \(j\) 使 \(i+dp_i<j-dp_j+1\) 且有 \(s_{i, i+dp_i-2}=s_{j-dp_j+1}\)\(s_{i+1, i+dp_j-1}=s_{j-dp_j+1}\)
还要求 \(dp_j\geqslant dp_i-1\)
首先 \(i+dp_i\) 不增,前一个条件还是可以单调指针解决
那后面两个条件发现放到sam上就是查询两个状态的fail树子树中的最大值

  • 给定一个串,多次询问,每次给定一个区间 \([l, r]\),要求关于快速找到sam上 \(s_{l, r}\) 对应的子串对应的状态:
    将原串插入sam时记录 \(pos_i\) 为第 \(i\) 个前缀的位置
    将问题转化为从 \(pos_r\) 向上跳到一个 \(len=r-l+1\) 的节点
    于是在fail树上倍增,跳跃的条件是 fa[j][u] && len[fa[j][u]]>=r-l+1

然后这题卡常,需要zkw线段树
本来准备学一下的结果写了个拍把网上的大部分板子(包括skyh的)都hack了又懒得自己再总结一个板子了于是自闭

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define ll long long
#define fir first
#define sec second
#define ull unsigned long long
//#define int long long

int n;
char s[N];

// namespace force{
// 	const ull base=13131;
// 	ll h[N], p[N];
// 	int ans;
// 	inline ull hashing(int l, int r) {return h[r]-h[l-1]*p[r-l+1];}
// 	unordered_map<ull, int> mp[N];
// 	void solve() {
// 		p[0]=1;
// 		for (int i=1; i<=n; ++i) p[i]=p[i-1]*base;
// 		for (int i=1; i<=n; ++i) h[i]=h[i-1]*base+s[i];
// 		for (int i=n; i; --i) {
// 			for (int j=i; j<=n; ++j) {
// 				if (clock()>900000) {printf("%d\n", ans); exit(0);}
// 				ull t=hashing(i, j);
// 				int now=1;
// 				for (int k=i; k<=j; ++k) {
// 					for (int l=k; l<=j; ++l) if (!(k==i&&l==j)) {
// 						ull t2=hashing(k, l);
// 						int tem=0;
// 						for (int v=j+1; v<=n; ++v)
// 							if (mp[v].find(t2)!=mp[v].end())
// 								tem=max(tem, mp[v][t2]);
// 						now=max(now, tem+1);
// 					}
// 				}
// 				mp[i][t]=max(mp[i][t], now);
// 				ans=max(ans, now);
// 			}
// 		}
// 		printf("%d\n", ans);
// 		exit(0);
// 	}
// }

// namespace task1{
// 	int rot[N], ans;
// 	namespace sam{
// 		int head[N], size;
// 		int len[N<<1], fail[N<<1], tr[N<<1][26], now, tot;
// 		map<int, int> dp[N<<1];
// 		set<int> st[N<<1];
// 		struct edge{int to, next;}e[N<<1];
// 		inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
// 		void init() {now=tot=size=0; memset(head, -1, sizeof(head)); fail[0]=-1;}
// 		void insert(char c, int pos) {
// 			c-='a';
// 			int cur=++tot;
// 			len[cur]=len[now]+1;
// 			int p, q;
// 			for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
// 			if (p==-1) fail[cur]=0;
// 			else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
// 			else {
// 				int cln=++tot;
// 				len[cln]=len[p]+1;
// 				fail[cln]=fail[q];
// 				for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
// 				for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
// 				fail[cur]=fail[q]=cln;
// 			}
// 			now=cur;
// 			// seg::upd(rot[cur], 1, n, i, 1);
// 			st[cur].insert(pos);
// 		}
// 		void dfs(int u) {
// 			set<int>::iterator tem;
// 			if (st[u].size()) dp[u][1]=*st[u].begin(); //, cout<<"add: "<<(*st[u].begin())<<endl;
// 			for (int i=head[u],v; ~i; i=e[i].next) {
// 				v = e[i].to;
// 				dfs(v);
// 				// seg::merge(rot[u], rot[v], 1, n);
// 				if (st[v].size()>st[u].size()) swap(st[u], st[v]);
// 				for (auto it:st[v]) st[u].insert(it);
// 			}
// 			cout<<"st: "<<u<<endl;
// 			for (auto it:st[u]) cout<<it<<' '; cout<<endl;
// 			if (!u) return ;
// 			for (int k=len[u]; k>len[fail[u]]; --k) {
// 				for (int i=head[u],v; ~i; i=e[i].next) {
// 					v = e[i].to;
// 					for (auto it:dp[v]) {
// 						if (it.sec+k<=n && (tem=st[u].lower_bound(it.sec+k))!=st[u].end()) {
// 							// cout<<"tem: "<<*tem<<endl;
// 							if (dp[u].find(it.fir+1)==dp[u].end()) dp[u][it.fir+1]=*tem;
// 							else dp[u][it.fir+1]=min(dp[u][it.fir+1], *tem);
// 						}
// 						if (dp[u].find(it.fir)==dp[u].end()) dp[u][it.fir]=it.sec;
// 						else dp[u][it.fir]=min(dp[u][it.fir], it.sec);
// 					}
// 				}
// 			}
// 			cout<<"dp: "<<u<<endl;
// 			for (auto it:dp[u]) cout<<it.fir<<','<<it.sec<<' '; cout<<endl;
// 			for (int k=len[u]; k>len[fail[u]]+1; --k) {
// 				for (auto it:dp[u]) {
// 					if (it.sec+(k-1)<=n && (tem=st[u].lower_bound(it.sec+(k-1)))!=st[u].end()) {
// 						if (dp[u].find(it.fir+1)==dp[u].end()) dp[u][it.fir+1]=*tem;
// 						else dp[u][it.fir+1]=min(dp[u][it.fir+1], *tem);
// 					}
// 				}
// 			}
// 		}
// 	}
// 	void solve() {
// 		using namespace sam;
// 		init();
// 		for (int i=1; i<=n; ++i) insert(s[i], i);
// 		for (int i=1; i<=tot; ++i) add(fail[i], i);
// 		dfs(0);
// 		for (int i=head[0],u; ~i; i=e[i].next) {
// 			u = e[i].to;
// 			for (auto it:dp[u]) ans=max(ans, it.fir);
// 		}
// 		printf("%d\n", ans);
// 		exit(0);
// 	}
// }

namespace task{
	int head[N<<1], in[N<<1], siz[N<<1], lg[N<<1], dep[N<<1], size;
	int dp[N], pos[N], deg[N<<1], tem[N<<1], fa[20][N], ans=1;
	int len[N<<1], fail[N<<1], tr[N<<1][26], now, tot, ord;
	struct edge{int to, next;}e[N<<1];
	inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
	namespace sam{
		void init() {now=tot=size=0; memset(head, -1, sizeof(head)); fail[0]=-1;}
		void insert(char c, int i) {
			c-='a';
			int cur=++tot;
			len[cur]=len[now]+1;
			int p, q;
			for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
			if (p==-1) fail[cur]=0;
			else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
			else {
				int cln=++tot;
				len[cln]=len[p]+1;
				fail[cln]=fail[q];
				for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
				for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
				fail[cur]=fail[q]=cln;
			}
			now=cur;
			pos[i]=cur;
		}
		void dfs(int u) {
			siz[u]=1; in[u]=++ord;
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				fa[0][v]=u; dep[v]=dep[u]+1;
				dfs(v);
				siz[u]+=siz[v];
			}
		}
	}
	// namespace zkw{
	// 	int tl[N<<2], tr[N<<2], dat[N<<2];
	// 	#define tl(p) tl[p]
	// 	#define tr(p) tr[p]
	// 	#define dat(p) dat[p]
	// 	#define pushup(p) dat(p)=max(dat(p<<1), dat(p<<1|1))
	// 	void build(int p, int l, int r) {
	// 		tl(p)=l; tr(p)=r; dat(p)=0;
	// 		if (l==r) return ;
	// 		int mid=(l+r)>>1;
	// 		build(p<<1, l, mid);
	// 		build(p<<1|1, mid+1, r);
	// 		pushup(p);
	// 	}
	// 	void upd(int p, int pos, int val) {
	// 		if (tl(p)==tr(p)) {dat(p)=val; return ;}
	// 		int mid=(tl(p)+tr(p))>>1;
	// 		if (pos<=mid) upd(p<<1, pos, val);
	// 		else upd(p<<1|1, pos, val);
	// 		pushup(p);
	// 	}
	// 	int query(int p, int l, int r) {
	// 		if (l<=tl(p)&&r>=tr(p)) return dat(p);
	// 		int mid=(tl(p)+tr(p))>>1, ans=-1;
	// 		if (l<=mid) ans=max(ans, query(p<<1, l, r));
	// 		if (r>mid) ans=max(ans, query(p<<1|1, l, r));
	// 		return ans;
	// 	}
	// }
	// namespace zkw{
	// 	int M, sum[N<<2], maxn[N<<2];
	// 	void build(int p, int l, int n) {
	// 		for (M=1; M<=n+1; M<<=1);
	// 		// for (int i=M-1; i; --i) {
	// 		// 	sum[i]=sum[i<<1]+sum[i<<1|1];
	// 		// 	maxn[i]=max(maxn[i<<1], maxn[i<<1|1]);
	// 		// 	maxn[i<<1]-=maxn[i], maxn[i<<1|1]-=maxn[i];
	// 		// }
	// 	}
	// 	void upd(int p, int x, int val) {
	// 		int k;
	// 		for (x+=M; x; x>>=1) {
	// 			sum[x]+=val; maxn[x]+=val;
	// 			k=max(maxn[x], maxn[x^1]);
	// 			maxn[x]-=k; maxn[x^1]-=k; maxn[x>>1]+=k;
	// 		}
	// 	}
	// 	int query(int p) {
	// 		int ans=0;
	// 		for (p+=M,ans=sum[p],p>>=1; p; p>>=1) ans+=0;
	// 		return ans;
	// 	}
	// 	int query(int p, int l, int r) {
	// 		// assert(l);
	// 		if (l==r) return query(l);
	// 		int L=0, R=0;
	// 		for (l+=M,r+=M; l^r^1; l>>=1,r>>=1) {
	// 			L+=maxn[l], R+=maxn[r];
	// 			if (~l&1) L=max(L, maxn[l^1]);
	// 			if (r&1) R=max(R, maxn[r^1]);
	// 		}
	// 		int ans=max(L+maxn[l], R+maxn[r]);
	// 		for (l>>=1; l; l>>=1) ans+=maxn[l];
	// 		return ans;
	// 	}
	// }
	// namespace zkw{
	// 	int n,bit;
	// 	int sum[N<<2],add[N<<2],mn[N<<2],mx[N<<2];
	// 	inline void build(int a1, int a2, int t){
	// 		n=t;
	// 		for(bit=1;bit<=n+1;bit<<=1);
	// 		for(int i=bit+1;i<=bit+n;++i) mx[i]=mn[i]=sum[i]=0;
	// 		for(int i=bit-1;i;--i){
	// 			sum[i]=sum[i<<1]+sum[i<<1|1];
	// 			mn[i]=min(mn[i<<1],mn[i<<1|1]); mn[i<<1]-=mn[i]; mn[i<<1|1]-=mn[i];
	// 			mx[i]=max(mx[i<<1],mx[i<<1|1]); mx[i<<1]-=mx[i]; mx[i<<1|1]-=mx[i];
	// 		}
	// 	}
	// 	inline int query(int p){
	// 		int ans=0;
	// 		for(p+=bit,ans=sum[p],p>>=1;p;p>>=1) ans+=add[p];
	// 		return ans;
	// 	}
	// 	inline int query_sum(int t, int l,int r){
	// 		int ans=0,lc=0,rc=0,len=1;
	// 		for(l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1,len<<=1){
	// 			ans+=add[l]*lc+add[r]*rc;
	// 			if(~l&1) ans+=sum[l^1],lc+=len;
	// 			if(r&1) ans+=sum[r^1],rc+=len;
	// 		}
	// 		for(;l;l>>=1,r>>=1) ans+=add[l]*lc+add[r]*rc;
	// 		return ans;
	// 	}
	// 	inline int query_min(int t, int l,int r){
	// 		if(l==r) return query(l);
	// 		int lans=0,rans=0;
	// 		for(l+=bit,r+=bit;l^r^1;l>>=1,r>>=1){
	// 			lans+=mn[l]; rans+=mn[r];
	// 			if(~l&1) lans=min(lans,mn[l^1]);
	// 			if(r&1) rans=min(rans,mn[r^1]);
	// 		}
	// 		for(lans=min(lans+mn[l],rans+mn[r]),l>>=1;l;l>>=1) lans+=mn[l];
	// 		return lans;
	// 	}
	// 	inline int query(int t, int l,int r){
	// 		if(l==r) return query(l);
	// 		int lans=0,rans=0;
	// 		for(l+=bit,r+=bit;l^r^1;l>>=1,r>>=1){
	// 			lans+=mx[l]; rans+=mx[r];
	// 			if(~l&1) lans=max(lans,mx[l^1]);
	// 			if(r&1) rans=max(rans,mx[r^1]);
	// 		}
	// 		for(lans=max(lans+mx[l],rans+mx[r]),l>>=1;l;l>>=1) lans+=mx[l];
	// 		return lans;
	// 	}
	// 	inline void modify(int t, int l,int r,int val){
	// 		int lc=0,rc=0,len=1,x;
	// 		for(l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1,len<<=1){
	// 			sum[l]+=lc*val; sum[r]+=rc*val;
	// 			if(~l&1) sum[l^1]+=len*val,add[l^1]+=val,mn[l^1]+=val,lc+=len;
	// 			if(r&1) sum[r^1]+=len*val,add[r^1]+=val,mn[r^1]+=val,rc+=len;
	// 			x=min(mn[l],mn[l^1]); mn[l]-=x; mn[l^1]-=x; mn[l>>1]+=x;
	// 			x=min(mn[r],mn[r^1]); mn[r]-=x; mn[r^1]-=x; mn[r>>1]+=x;
	// 		}
	// 		for(;l;l>>=1,r>>=1){
	// 			sum[l]+=lc*val; sum[r]+=rc*val;
	// 			x=min(mn[l],mn[l^1]); mn[l]-=x; mn[l^1]-=x; mn[l>>1]+=x;
	// 			x=max(mx[l],mx[l^1]); mx[l]-=x; mx[l^1]-=x; mx[l>>1]+=x;
	// 		}
	// 	}
	// 	inline void upd(int t, int p,int val){
	// 		int x;
	// 		for(p+=bit;p;p>>=1){
	// 			sum[p]+=val; mn[p]+=val; mx[p]+=val;
	// 			x=min(mn[p],mn[p^1]); mn[p]-=x; mn[p^1]-=x; mn[p>>1]+=x;
	// 			x=max(mx[p],mx[p^1]); mx[p]-=x; mx[p^1]-=x; mx[p>>1]+=x;
	// 		}
	// 	}
	// }
	namespace zkw{
		int M, mx[N<<2];
		void build(int a, int b, int n) {for (M=1; M<=n; M<<=1);}
		void upd(int t, int x, int v) {
			x+=M;
			for (; x; x>>=1) mx[x]=max(mx[x], v);
		}
		int query(int t, int l, int r) {
			int ans=0;
			for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1) {
				if (~l&1) ans=max(ans, mx[l+1]);
				if (r&1) ans=max(ans, mx[r-1]);
			}
			return ans;
		}
	}
	int anc(int u, int k) {
		for (int j=lg[dep[u]]-1; ~j; --j)
			if (fa[j][u] && len[fa[j][u]]>=k)
				u=fa[j][u];
		return u;
	}
	bool check(int i, int l) {
		// cout<<"check: "<<i<<' '<<l<<endl;
		int t1=anc(pos[i], l-1), t2=anc(pos[i-1], l-1);
		// cout<<"pos: "<<pos[i]<<' '<<pos[i-1]<<endl;
		// cout<<"t: "<<t1<<' '<<t2<<endl;
		// cout<<"in: "<<in[t1]<<' '<<in[t1]+siz[t1]-1<<' '<<seg::query(1, in[t1], in[t1]+siz[t1]-1)<<endl;
		// cout<<"in: "<<in[t2]<<' '<<in[t2]+siz[t2]-1<<' '<<seg::query(1, in[t2], in[t2]+siz[t2]-1)<<endl;
		if (zkw::query(1, in[t1], in[t1]+siz[t1]-1)>=l-1) return 1;
		if (zkw::query(1, in[t2], in[t2]+siz[t2]-1)>=l-1) return 1;
		return 0;
	}
	void solve() {
		sam::init();
		reverse(s+1, s+n+1);
		for (int i=1; i<=n; ++i) sam::insert(s[i], i);
		for (int i=1; i<=tot; ++i) add(fail[i], i); //, cout<<"add: "<<fail[i]<<' '<<i<<endl;
		for (int i=1; i<=tot; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		sam::dfs(0);
		zkw::build(1, 1, tot+1);
		// cout<<"build: "<<1<<' '<<tot<<endl;
		for (int i=1; i<=tot; ++i) ++deg[len[i]];
		for (int i=1; i<=tot; ++i) deg[i]+=deg[i-1];
		for (int i=1; i<=tot; ++i) tem[deg[len[i]]--]=i;
		for (int i=1,t=tem[1]; i<=tot; t=tem[++i])
			for (int j=1; j<=20; ++j)
				if (!(fa[j][t]=fa[j-1][fa[j-1][t]]))
					break;
		int lim=1;
		dp[1]=1;
		for (int i=2; i<=n; ++i) {
			dp[i]=dp[i-1]+1;
			while (!check(i, dp[i])) {
				--dp[i];
				for (; lim<=i-dp[i]; ++lim) zkw::upd(1, in[pos[lim]], dp[lim]); //, cout<<"upd: "<<lim<<' '<<pos[lim]<<' '<<in[pos[lim]]<<' '<<dp[lim]<<endl;
			}
			for (; lim<=i-dp[i]; ++lim) zkw::upd(1, in[pos[lim]], dp[lim]); //, cout<<"upd: "<<lim<<' '<<pos[lim]<<' '<<in[pos[lim]]<<' '<<dp[lim]<<endl;
			ans=max(ans, dp[i]);
		}
		// cout<<"pos: "; for (int i=1; i<=n; ++i) cout<<pos[i]<<' '; cout<<endl;
		// cout<<"dp: "; for (int i=1; i<=n; ++i) cout<<dp[i]<<' '; cout<<endl;
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	scanf("%s", s+1);
	n=strlen(s+1);
	// force::solve();
	task::solve();
	
	return 0;
}
posted @ 2021-12-13 20:22  Administrator-09  阅读(2)  评论(0编辑  收藏  举报