1153 - 无影的神之右手(莫队算法)

本文介绍了一道算法题目“无影的神之右手”,任务是查询区间内序列元素乘积的约数个数模19260817。通过素数分解和特殊处理大于1000的因子,利用莫队算法解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1153 - 无影的神之右手

Time Limit:4s Memory Limit:512MByte

Submissions:261Solved:31

DESCRIPTION




觉不觉得这几个图很有毒啊?
其实这几个不算很有毒吧~
由乃懒得写题面了,反正写了也没人看,所以直接说题意吧~
给你一个序列a,每次查询一个区间[l,r]的乘积的约数个数mod 19260817

INPUT
第一行两个数n,m第二行n个数表示序列a后面m行每行两个数l,r表示查询区间[l,r]
OUTPUT
对于每个查询输出答案
SAMPLE INPUT
5 564 2 18 9 1001 52 42 31 43 4
SAMPLE OUTPUT
1651594510
HINT
n , m <= 100000 , a[i] <= 1000000
SOLUTION

“玲珑杯”ACM比赛 Round #20

解题思路:我们首先对每个数进行素数分解,大于1000的因子最多只有一个,小于1000的因子我们暴力记录,然后对于大于1000的因子用莫队处理就好。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
using namespace std;
typedef long long LL;
inline bool scan_d(int &num)
{
        char in;bool IsN=false;
        in=getchar();
        if(in==EOF) return false;
        while(in!='-'&&(in<'0'||in>'9')) in=getchar();
        if(in=='-'){ IsN=true;num=0;}
        else num=in-'0';
        while(in=getchar(),in>='0'&&in<='9'){
                num*=10,num+=in-'0';
        }
        if(IsN) num=-num;
        return true;
}
const int maxn = 100000 + 10;
const LL mod = 19260817;
int n, m;
LL s0;
int num[1000005];
int a[maxn];
int b[maxn];//大于1000的质因子
LL term[maxn];
int L, R;
bool valid[10005];
int prime[10005];
int dp[maxn][200];//dp[i][j]前i个数,质因子为primep[j]的个数
int inv[100005];
int pos[maxn];
int tot;
int judge;
int block;
struct query
{
    int l, r;
    int id;
    int loc;
    bool operator <(const query &res) const
    {
        if(loc == res.loc) return r < res.r;
        else return loc < res.loc;
    }

}Query[maxn];
LL quick_pow(LL x, LL i)
{
    LL ans = 1;
    x %= mod;
    if(x == 1) return 1;
    while(i)
    {
        if(i&1) ans = (ans * x) % mod;
        i >>= 1;
        x = (x * x) % mod;
    }
    return ans;
}
void getPrime()
{
    memset(valid, true, sizeof(valid));
    tot = 0;
    for(int i = 2; i <= 10000; i++)
    {
        if(valid[i])
        {
            prime[++tot] = i;
        }
        for(int j = 1; j <= tot && prime[j] * i <= 10000; j++)
        {
            valid[i * prime[j]] = false;
            if(i % prime[j] == 0) break;
        }
    }
    for(int i = 1; i <= tot; i++)
    {
        if(prime[i] > 1000)
        {
            judge = i - 1;
            break;
        }
    }
    for(int i = 1; i < 100004; i++)
    {
        inv[i] = (int)quick_pow((LL)i, mod - 2);
    }
}
void init()
{
    L = 1;
    R = 1;
    memset(num, 0, sizeof(num));
    memset(dp, 0, sizeof(dp));
    memset(b, 0, sizeof(b));
    block = sqrt(n);
    s0 = 1;
}
void add(int x)
{
    LL temp = (LL)inv[num[x] + 1];
    s0 = (s0 * temp) % mod;
    num[x]++;
    s0 = (s0 * (num[x] + 1)) % mod;

}
void sub(int x)
{
    LL temp = (LL)inv[num[x] + 1];
    s0 = (s0 * temp) % mod;
    num[x]--;
    s0 = (s0 * (num[x] + 1)) % mod;
}
int main()
{
    //freopen("C:\\Users\\creator\\Desktop\\A2.in","r",stdin) ;
    //freopen("C:\\Users\\creator\\Desktop\\out.txt","w",stdout) ;
    getPrime();
    //cout<<"judge == "<<judge<<endl;
    while(~scanf("%d%d", &n, &m))
    {
        //printf("n == %d m == %d\n", n, m);
        init();
        for(int i = 1; i <= n; i++)
        {
            //scanf("%d", &a[i]);
            scan_d(a[i]);
            pos[i] = i / block;
        }
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= judge; j++)
            {
                int temp = 0;
                while(a[i] % prime[j] == 0)
                {
                    temp++;
                    a[i] /= prime[j];
                }
                dp[i][j] = dp[i - 1][j] + temp;
            }
            if(a[i] != 1) b[i] = a[i];

        }
        for(int i = 1; i <= m; i++)
        {
            //scanf("%d%d", &Query[i].l, &Query[i].r);
            scan_d(Query[i].l);
            scan_d(Query[i].r);
            Query[i].id = i;
            Query[i].loc = pos[Query[i].l];
        }
        sort(Query + 1, Query + m + 1);
        if(b[1] != 0) add(b[1]);
        for(int i = 1; i <= m; i++)
        {

            int l = Query[i].l;
            int r = Query[i].r;
            while(R < r)
            {
                R++;
                if(b[R])
                {
                    add(b[R]);
                }
            }
            while(L > l)
            {
                L--;
                if(b[L])
                {
                    add(b[L]);
                }
            }
            while(L < l)
            {
                if(b[L])
                {
                    sub(b[L]);
                }
                L++;
            }
            while(R > r)
            {
                if(b[R])
                {
                    sub(b[R]);
                }
                R--;
            }
            LL re = 1;
            for(int j = 1; j <= judge; j++)
            {
                LL d = (LL)(dp[r][j] - dp[l - 1][j]);
                if(d) re = (re * (d + 1)) % mod;
            }
            re = (re * s0) % mod;
            term[Query[i].id] = re;
        }
        for(int i = 1; i <= m; i++)
        {
            printf("%lld\n", term[i]);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值