codeforces 992 D Nastya and a Game 【数论 暴力剪枝】

题目大意:
给你一个n,k ,接着给你n个数,让你找到这样一段连续子序列,ai, ai+1,……,aj ,使得这段序列的乘积p 除以 该序列的和s 的值恰好为k
n的数据范围为2e5 k的数据范围 1e5 ,ai的数据范围是1e8
题目分析
我们发现对于这个乘积p ,不可能无穷大,当我们取最大的s*k时也就是 1e8*2e5*1e5 =2e18,这个数据范围正好不超过longlong,那么我们假想一下,在一段子序列中大于1的数最多不会超过64个,那么我们只要枚举左端点,然后往后跳,跳到非1的数的位置,然后暴力判断即可。
题目难点:
这个题目有两个值得注意的点,一个就是ll溢出的判断,可以采用 LLONGMAX的方式,还可以采用转化成double的方式
还有就是跳跃的方式,可以用一个数组vis[i] 记录我们要跳跃的位置,vis[i+1]存储的坐标就是下一个非1的位置,我们只需要倒过来扫一遍就可以

代码:

#include <bits/stdc++.h>
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn=2e5+50;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
typedef long long ll;
ll n,k;
ll a[maxn];
int vis[maxn];
ll ans=0;
bool Overflow(ll x,ll y)
{
    if(x>LLONG_MAX/y)return false;
    else return true;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cl(vis);
    cl(a);
    cin>>n>>k;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }
    vis[n+1] = -1;
    for(int i=n; i>=1; i--)
    {
        if(a[i]>1) vis[i]=i;
        else vis[i]=vis[i+1];
    }
    ll p =1;
    ll s=0;
    for(int i=1; i<=n; i++)
    {
        int j=i;
        p=a[i];
        s=a[i];
        if(p==s*k)ans++;
        while(1)
        {
            int z = vis[j+1];
            if(z==-1)z=n+1;
            int ones = z-j-1;
            if(p%k==0&&p/k-s>=1&&p/k-s<=ones)
            {
                ans++;
            }
            if(z==n+1)break;
            if(Overflow(p,a[z]))
            {
                p = p*a[z];
                j = z;
                s = s + a[z] +ones;
                if(p == s * k) ans++;
            }
            else break;
        }
    }
    cout<<ans<<endl;
    return 0;
}
全部评论

相关推荐

牛客717484937号:双飞硕没实习挺要命的
点赞 评论 收藏
分享
10-30 23:23
已编辑
中山大学 Web前端
去B座二楼砸水泥地:这无论是个人素质还是专业素质都👇拉满了吧
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务