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;
}

浙公网安备 33010602011771号