题目链接:Problem - 818E - Codeforces Card Game Again
你需要从给定的 n n n个数中,去掉前 x x x( x > = 0 x>=0 x>=0),后 y y y( y > = 0 y>=0 y>=0),且 x + y < n x+y<n x+y<n,剩下的数的乘积如果是 k k k的整数倍,那么我们称它为生动的序列,需要求有多少个生动的序列。由于数据量比较大,我们不能直接计算乘积而转化为对 k k k进行因式分解。整个序列对其因子进行分解,记下数量。知道这一步转化后,我们就该思考如何计数了,这道题我的思路是尺取。对于每一个给定的 x x x,我们只需要求 x + 1 x+1 x+1开始的连续序列,直到 r r r满足刚好他们的乘积是 k k k的整数倍(即对应的k中的所有因子累积和都大于k自身的(比如,6的因子为2和3,只要从x+1开始的连续序列的2因子的数量与3因子的数量累积和大于1))。这个累积和类似于前缀和是单调递增的,我们可以用二分来实现查询操作最终的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
#include<bits/stdc++.h>
#define fi first
#define se second
const int N=1e5+5;
using namespace std;
int n,k;
vector<pair<int,int>> g;
int a[N][100],sum[N][100];
bool check(int x,int y){
bool flag=true;
for(int i=0;i<g.size();i++){
if(sum[y][i]-sum[x][i]<g[i].se){
flag=false;
break;
}
}
return flag;
}
int main(){
scanf("%d%d",&n,&k);
if(k==1){
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
}
printf("%lld\n",1ll*n*(1+n)/2);
return 0;
}
int mm=n;
int len=(int)sqrt(k)+1;
int res=k;
for(int i=2;i<=len&&k>1;i++){
int num=1;
if(k%i==0){
g.push_back(make_pair(i,num++));
k/=i;
}
while(k%i==0){
g[g.size()-1]=make_pair(i,num++);
k/=i;
}
}
if(k>1){
g.push_back(make_pair(k,1));
}
// for(int i=0;i<g.size();i++){
// printf("%d %d\n",g[i].first,g[i].second);
// }
int f=0;
while(n--){
int x;
scanf("%d",&x);
int flag=0;
f++;
for(int i=0;i<g.size()&&x>1;i++){
int num=1;
if(x%g[i].fi==0){
a[f][i]=num++;
x/=g[i].fi;
}
else continue;
while(x%g[i].fi==0){
a[f][i]=num++;
x/=g[i].fi;
}
}
}
n=mm;
for(int i=1;i<=n;i++){
for(int j=0;j<g.size();j++){
sum[i][j]=sum[i-1][j]+a[i][j];
}
}
// for(int i=1;i<=n;i++){
// for(int j=0;j<g.size();j++){
// printf("#%d %d\n",j,sum[i][j]);
// }
// printf("\n");
// }
long long ans=0;
for(int i=0;i<=n-1;i++){
int l=i+1,r=n,zz=l-1;
if(!check(zz,r)) break;
int mid;
while(l<r){
mid=l+r>>1;
if(check(zz,mid)){
r=mid;
}
else l=mid+1;
}
ans+=n-r+1;
}
printf("%lld\n",ans);
return 0;
}
这道题的思路一开始就想到,这代码是真的给我恶心吐了。