P6360 [CEOI2018] Lottery 题解

P6360 [CEOI2018] Lottery 题解


题意分析

给一段长度为 \(n\) 的数列 \(\{ a_i \}\),子段长度 \(l\)\(q\) 个询问。

每次询问输入 \(k\),求与每一段长度为 \(l\) 的子段数字差别个数 \(\le k\) 的子段个数(除自己外)。


思路分析

首先,直接暴力比较每一个子段,再用前缀和统计,可以做到时间复杂度 \(O(n^2l)\),空间复杂度 \(O(n^2)\)

考虑优化:观察数据范围:\(1 \le n \le 10^4,1 \le q \le 10^2\),那么程序是可以支持时间复杂度 \(O(n^2)\) 的算法的。

于是我们双指针移动区间进行比较,每次钦定两个子段的距离即可,最后前缀和,时间复杂度 \(O(n^2)\),空间复杂度 \(O(n^2)\)

可是,“请注意特殊的内存限制”,空间复杂度 \(O(n^2)\) 是不行的,那怎么办呢?

我们发现 \(O(nq)\) 的空间复杂度是可以的,那么我们离线询问,直接将中间没有询问的差值叠加到一旁的询问即可。


CODE

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define EDGE(g,i,u,x) for(register int (i)=(g).h[(u)],(x)=(g).v[(i)];(i);(i)=(g).nxt[(i)],(x)=(g).v[(i)])
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
constexpr int N=1e4+10,Qr=1e2+10;
int n,m,Q;
int a[N],b[Qr],k[Qr],id[N];
int ans[Qr][N];
signed main(){
	cin>>n>>m;
	FOR(i,1,n)cin>>a[i];
	cin>>Q;
	FOR(i,1,Q)cin>>k[i],b[i]=k[i];
	sort(b+1,b+Q+1),b[0]=unique(b+1,b+Q+1)-b-1;
	FOR(i,1,b[0])id[b[i]]=i;
	id[m+1]=b[0]+1;
	DOR(i,m,0)if(!id[i])id[i]=id[i+1];
	FOR(len,1,n-m){
		int sum=0,it=1;
		FOR(i,1,n-len-m+1){
			for(;it<=i+m-1;++it)sum+=(a[it]!=a[it+len]);
			++ans[id[sum]][i],++ans[id[sum]][i+len];
			sum-=(a[i]!=a[i+len]);
		}
	}
	FOR(i,1,b[0])FOR(j,1,n-m+1)ans[i][j]+=ans[i-1][j];
	FOR(i,1,Q){
		FOR(j,1,n-m+1)cout<<ans[id[k[i]]][j]<<" ";
		cout<<endl;
	}
	return 0;
}
posted @ 2024-07-13 15:49  Add_Catalyst  阅读(20)  评论(0)    收藏  举报