• CF818E Solution


    题目链接

    题解

    ⭐:①可以将整除的条件转化为余数为\(0\)。②枚举左右端点可以找寻单调性,尝试固定一端二分另一端。

    暴力的话枚举左右端点,然后\(O(n)\)求乘积余数,总时间复杂度为\(O(n^3)\)。其中区间乘积求余可以使用线段树维护,又可发现,若区间\([i,j]\)的乘积可以被\(k\)整除,则对于\(j<k\le n,[i,k]\)均满足\(k\)整除其乘积。换言之,整除满足单调性。因此可以固定右端点二分左端点,找出满足整除的最小区间。对于每个最小区间\([l,r]\),其所在区间数\(=l\cdot (n-r+1)\),但如果将所有最小区间的该值相加,会有大量重复。因此当我们求出第\(i\)个最小区间\([l_i,r_i]\)时,为避免重复假设\([1,l_{i-1}]\)的元素已被删除,左侧只需考虑\((l_i-l_{i-1})\)个元素。

    AC代码

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=1e5+10;
    struct node {int l,r,sum;} tr[4*N];
    int a[N],k;
    void build(int x,int l,int r)
    {
    	tr[x].l=l,tr[x].r=r;
    	if(l==r) {tr[x].sum=a[l]%k; return;}
    	int mid=(l+r)/2;
    	build(x*2,l,mid),build(x*2+1,mid+1,r);
    	tr[x].sum=tr[x*2].sum*tr[x*2+1].sum%k;
    }
    int query(int x,int l,int r)
    {
    	if(tr[x].l>=l && tr[x].r<=r) return tr[x].sum;
    	int ans=1;
    	if(r>=tr[x*2+1].l) ans=ans*query(x*2+1,l,r)%k;
    	if(l<=tr[x*2].r) ans=ans*query(x*2,l,r)%k;
    	return ans;
    }
    signed main()
    {
    	int n,ans=0,lst=1;//lst:上文所述l[i-1]
    	scanf("%lld%lld",&n,&k);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    	build(1,1,n);
    	for(int i=1;i<=n;i++)//i:上文所述r[i]
    	{
    		int l=lst,r=i,pos=-1;//pos:上文所述l[i]
    		while(l<=r)
    		{
    			int mid=(l+r)/2;
    			if(!query(1,mid,i)) pos=mid,l=mid+1;
    			else r=mid-1;
    		} 
    		if(pos!=-1) {ans+=(pos-lst+1)*(n-i+1); lst=pos+1;}
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    3种方法实现CSS隐藏滚动条并可以滚动内容
    javascript 计算两个整数的百分比值
    使用watch监听路由变化和watch监听对象的实例
    springboot全局捕获异常
    使用 Java 创建聊天客户端-2
    使用 Java 创建聊天客户端-1
    使用 ServerSocket 建立聊天服务器-2
    使用 ServerSocket 建立聊天服务器-1
    ServerSocket
    scheduled定时任务+实例请求数据库
  • 原文地址:https://www.cnblogs.com/violetholmes/p/14530011.html
Copyright © 2020-2023  润新知