1 条题解

  • 0
    @ 2025-8-24 22:56:28

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar banned_gutongxing
    我会等枯树生出芽,开出新的花,等着阳光刺破黑暗第一缕朝霞。我会等一场雨落下,把回忆冲涮,再与你一起去看外面世界到底有多大。——《我会等》> 最后在线时间:2025年3月1日 <

    搬运于2025-08-24 22:56:28,当前版本为作者最后更新于2024-08-20 13:24:03,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    思路

    建议升蓝。

    算法一

    考虑暴力。

    我们先枚举 K,LK,L,考虑如何求解。

    直接枚举每一个 KK-mer,再枚举里面的每一个长度为 LL 的子串,找到最大的子串并在起始部分打一个标记。最后直接看有几个地方被打标记就行。

    时间复杂度:O(n4)O(n^4)。预计能过测试点 141-4

    算法二

    我们可以把选取子串的过程大概画下来。

    我们发现每一次都会往后面都会多一个子串,我们可以考虑一个数据结构,可以删除最前面的数据,而且可以往后面加入一个数据,并动态求最值。我在这里选择的数据结构为单调队列

    时间复杂度:O(n3)O(n^3),预计能过测试点 171-7

    代码

    实践后:

    常数写大了,超了 0.07秒

    #include<bits/stdc++.h>
    using namespace std;
    #define endl '\n'
    //#define int long long
    namespace gtx{
    //	Fast IO
    	void read(int &x){
    		x = 0;int h = 1;char tmp;
    		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
    		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
    		x*=h;
    	}
    	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
    	void write(char x){putchar(x);}
    	void write(int x){
    		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
    		do{st[++tot]=x%10,x/=10;} while(x);
    		while(tot){putchar(st[tot--]+'0');};
    	}
    	void write(int x,char y){write(x);write(y);}
    	const int MAXN = 3100;
    	int n,ans,t[MAXN];
    	char a[MAXN];
    	signed main(){
    		read(n);
    		for(int i = 1;i<=n;i++) read(a[i]);
    		for(int K = 1;K<=n;K++){
    			for(int L = 1;L<=K;L++){
    				deque<pair<int,string>> st;
    				string now;
    				for(int i = 1;i<=L;i++) now += a[i];
    				st.push_back({1,now});
    				for(int i = 2;i<=K-L+1;i++){
    					now.erase(now.begin());
    					now += a[i+L-1];
    					while(!st.empty()&&st.back().second>now) st.pop_back();
    					st.push_back({i,now});
    				}
    				bitset<MAXN> b;
    				b.set(st.front().first);
    				for(int i = K-L+2;i<=n-L+1;i++){
    					if(st.front().first==i-(K-L)-1) st.pop_front();
    					now.erase(now.begin());
    					now += a[i+L-1];
    					while(!st.empty()&&st.back().second>now) st.pop_back();
    					st.push_back({i,now});
    					b.set(st.front().first);
    				}
    				t[b.count()]++; 
    			}
    		}
    		for(int i = 1;i<=n;i++) write(t[i],'\n');
    		return 0;
    	}
    }
    signed main(){
    //	freopen("gene.in","r",stdin);
    //	freopen("gene.ans","w",stdout);
    //	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    	int T = 1;
    //	gtx::read(T);
    	while(T--) gtx::main();
    	return 0;
    }
    

    算法三

    我们在上面的代码中发现其实 KK 有没有都几乎没有什么区别,于是可以想一想定长的 LL 可以对哪些答案产生的贡献。

    记录前面第一个比这个子串大的子串的起始位置为 leftileft_i,后面第一个比这个子串大的子串的起始位置为 rightiright_i。那么对于一个子串来说,如果这个子串能产生贡献 KK 最大应该的值的区间应该是这样的:

    所以产生的最大的 KKrighti+L2leftiright_i+L-2-left_i。最小的 KK 应该就是这个子串的长度,那么这个子串就会对这些 KK 产生答案。我们可以用差分解决。

    时间复杂度:O(n2)O(n^2)。预计得分:100pts100pts

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    #define endl '\n'
    //#define int long long
    namespace gtx{
    //	Fast IO
    	void read(int &x){
    		x = 0;int h = 1;char tmp;
    		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
    		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
    		x*=h;
    	}
    	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
    	void write(char x){putchar(x);}
    	void write(int x){
    		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
    		do{st[++tot]=x%10,x/=10;} while(x);
    		while(tot){putchar(st[tot--]+'0');};
    	}
    	void write(int x,char y){write(x);write(y);}
    	const int MAXN = 3100;
    	int n,t[MAXN],ans[MAXN][MAXN],left[MAXN],right[MAXN];
    	char a[MAXN];
    	signed main(){
    		read(n);
    		for(int i = 1;i<=n;i++) read(a[i]);
    			for(int L = 1;L<=n;L++){
    				stack<pair<int,string>> st;
    				string now;
    				for(int i = 1;i<=L;i++) now += a[i];
    				st.push({1,now});
    				for(int i = 1;i<=n-L+1;i++) left[i] = 0;
    				for(int i = 1;i<=n-L+1;i++) right[i] = 0;
    				left[1] = 1;
    				for(int i = 2;i<=n-L+1;i++){
    					now.erase(now.begin());
    					now += a[i+L-1];
    					while(!st.empty()&&st.top().second>now) st.pop();
    					left[i] = st.empty()?1:st.top().first+1;
    					st.push({i,now});
    				}
    				while(!st.empty()) st.pop();
    				now="";
    				for(int i = n-L+1;i<=n;i++) now+=a[i];
    				right[n-L+1] = n;
    				st.push({n-L+1,now});
    				for(int i = n-L;i>=1;i--){
    					now.erase(--now.end());
    					now = a[i]+now;
    					while(!st.empty()&&st.top().second>=now) st.pop();
    					right[i] = st.empty()?n:st.top().first+L-2;
    					st.push({i,now});
    				}
    				for(int i = 1;i<=n-L+1;i++){
    					if(right[i]-left[i]+1<L) continue;
    					ans[L][L]++;
    					ans[right[i]-left[i]+2][L]--;
    				}
    //				cout << L << endl;
    //				for(int i = 1;i<=n;i++) cout << left[i] << " " << right[i] << endl;
    			}
    		for(int j = 1;j<=n;j++){
    			for(int i = j;i<=n;i++){
    				ans[i][j] += ans[i-1][j];
    				t[ans[i][j]]++;
    			}
    		}
    		for(int i = 1;i<=n;i++) write(t[i],'\n');
    		return 0;
    	}
    }
    signed main(){
    //	freopen("gene.in","r",stdin);
    //	freopen("gene.out","w",stdout);
    //	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    	int T = 1;
    //	gtx::read(T);
    	while(T--) gtx::main();
    	return 0;
    }
    
    • 1

    信息

    ID
    9922
    时间
    2000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者