Card Game Again(尺取+二分)

题目链接: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;
}

这道题的思路一开始就想到,这代码是真的给我恶心吐了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值