【bzoj5452】[Hnoi2016]大数(莫队)

  题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4542

  首先若p=2,5则这题就是道傻逼题,前缀和搞一下没了。如果p为其他质数,那么可以这么处理:

  我们先预处理出数组num[i]表示原串第i~n位表示的数模p的余数,那么第l~r位表示的数模p的余数为(num[l]-num[r+1])/10^(n-r),因为10^(n-r)与p互质,所以若num[l]=num[r+1],则第l~r位表示的数是p的倍数。于是莫队一下就好了。

  代码:

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define ull unsigned long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define lowbit(x) (x& -x)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define eps 1e-18
#define maxn 2000010
inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<‘0‘||‘9‘<c;c=getchar())if(c==‘-‘)f=-1; for(;‘0‘<=c&&c<=‘9‘;c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-‘0‘; return tmp*f;}
inline ll power(ll a,ll b){ll ans=1; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
using namespace std;
struct data{
    int l,r,id;
}a[100010];
struct data2{
    ll val;
    int id;
}x[100010];
char s[100010];
ll rk[100010],cnt[100010],ans[100010],sum1[100010],sum2[100010];
int n,m,size;
ll p,tot;
bool cmp(data a,data b){return a.l/size!=b.l/size?a.l/size<b.l/size:a.r<b.r;}
bool cmp2(data2 a,data2 b){return a.val<b.val;}
int main()
{
    p=read();
    scanf("%s",s); n=strlen(s); size=sqrt(n);
    m=read();
    if(p==2||p==5){
        sum1[0]=sum2[0]=0;
        for(int i=1;i<=n;i++){
            sum1[i]=sum1[i-1]; sum2[i]=sum2[i-1];
            if((s[i-1]-‘0‘)%p==0)++sum1[i],sum2[i]+=i;
        }
        for(int i=1;i<=m;i++){
            int l=read(),r=read();
            printf("%lld\n",sum2[r]-sum2[l-1]-(sum1[r]-sum1[l-1])*(l-1));
        }
        fclose(stdin); fclose(stdout);
        return 0;
    }
    for(int i=1;i<=m;i++)
        a[i].l=read()-1,a[i].r=read(),a[i].id=i;
    sort(a+1,a+m+1,cmp);
    ll tmp=1; x[n].val=0; x[n].id=n;
    for(int i=n-1;i>=0;i--,tmp=tmp*10%p)x[i].val=(x[i+1].val+(s[i]-‘0‘)*tmp)%p,x[i].id=i;
    sort(x,x+n+1,cmp2);
    rk[x[0].id]=0;
    for(int i=1;i<=n;i++)
        if(x[i].val==x[i-1].val)rk[x[i].id]=rk[x[i-1].id];
        else rk[x[i].id]=i;
    tot=0;
    for(int i=a[1].l;i<=a[1].r;i++)
        tot+=cnt[rk[i]]++;
    ans[a[1].id]=tot;
    for(int i=2;i<=m;i++){
        if(a[i-1].l<a[i].l){
            for(int j=a[i-1].l;j<a[i].l;j++)
                tot-=--cnt[rk[j]];
        }
        else{
            for(int j=a[i].l;j<a[i-1].l;j++)
                tot+=cnt[rk[j]]++;
        }
        if(a[i-1].r<a[i].r){
            for(int j=a[i-1].r+1;j<=a[i].r;j++)
                tot+=cnt[rk[j]]++;
        }
        else{
            for(int j=a[i].r+1;j<=a[i-1].r;j++)
                tot-=--cnt[rk[j]];
        }
        ans[a[i].id]=tot;
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
}

bzoj4542

原文地址:https://www.cnblogs.com/quzhizhou/p/9735726.html

时间: 2024-10-30 00:18:25

【bzoj5452】[Hnoi2016]大数(莫队)的相关文章

【BZOJ4542】[Hnoi2016]大数 莫队

[BZOJ4542][Hnoi2016]大数 Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345.小B还有一个素数P.现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也是P 的倍数).例如 S为0077时,其子串 007有6个子串:0,0,7,00,07,007:显然0077的子串007有6个子串都是素数7的倍数. Input 第一行一个整数:P.第二行一个串:S

4542: [Hnoi2016]大数|莫队

HN一天考两个莫队是什么鬼..或者说莫队不是正确的姿势..? 考虑已经知道了l..r的答案新添入r+1如何更新当前答案 需要先预处理出后缀modp的值bi,假设子序列l..r模p的值为x 那么x?10r?l+b[r]=b[l] 然后就可以直接莫队统计了 模数为2或5的时候要特判一下 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cs

[BZOJ4542] [Hnoi2016] 大数 (莫队)

Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345.小B还有一个素数P.现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也是P 的倍数).例如 S为0077时,其子串 007有6个子串:0,0,7,00,07,007:显然0077的子串007有6个子串都是素数7的倍数. Input 第一行一个整数:P.第二行一个串:S.第三行一个整数:M.接下来M行,每行两个整数

[BZOJ4542] [JZYZOJ2014][Hnoi2016] 大数(莫队+离散化)

正经题解在最下面 http://blog.csdn.net/qq_32739495/article/details/51286548 写的时候看了大神的题解[就是上面那个网址],看到下面这段话 观察题目,发现一串数s(l~r)整除p满足s(l~n-1)%p==s(r+1~n-1)%p 但p值为2或5不满足这个性质需要特判(不过数据中好像没有,于是笔者没写,有兴趣的可以自己去写写......) 然后问题转化为求一段区间中有几对相等的f值. 看到这里,我感觉豁然开朗,完全忽视了离散化的要求,我以为把

4540: [Hnoi2016]序列|莫队+ST表

考虑现在已经知道了[l,r]的答案新添入一个r+1如何更新答案 也就是右端点在r+1处左端点在l..r+1之间的所有的子序列的答案 可以找出l..r中最小的数的位置p,然后p以及p左侧作为左端点的答案就可以直接计算了 考虑左端点在p+1....r+1时对答案的贡献,可以与处理一个前缀和Si表示以i为右端点的所有子序列的答案之和 那么左端点在p+1....r+1时对答案的贡献就是Sr+1?Sp 其他端点移动的做法也同理 为什么我的莫队跑了17s,而网上的其他莫队只需要5s,人傻自带三倍常数QWQ

【莫队】bzoj4542: [Hnoi2016]大数

挺有意思的,可以仔细体味一下的题:看白了就是莫队板子. Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345.小B还有一个素数P.现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也是P 的倍数).例如 S为0077时,其子串 007有6个子串:0,0,7,00,07,007:显然0077的子串007有6个子串都是素数7的倍数. Input 第一行一个整数:P.第二行

【BZOJ4540】【HNOI2016】序列(莫队)

[BZOJ4540][HNOI2016]序列(莫队) 题面 BZOJ 洛谷 Description 给定长度为n的序列:a1,a2,-,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,-,ar- 1,ar.若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列.现在有q个询问,每个询问给定两个数l和r,1≤l≤r ≤n,求a[l:r]的不同子序列的最小值之和.例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有 6个子序

[bzoj4540][Hnoi2016]序列——单调栈+莫队+RMQ

题目大意: 给定一个序列,每次询问一个区间[L,R]所有子区间的最小值之和. 思路: 考虑莫队如何转移,新增一个端点R,则增加的区间为[L...R-1,R],考虑这些区间新贡献的最小值. 我们把从R开始向左单调下降的序列给求出来,不难发现最小值是由区间内包含的最靠左一个在单调下降序列里的元素的值所决定的. 于是我们利用单调栈求出每一个元素前面第一个小于它的元素\(pre_i\),并求出以这个元素结尾的所有区间的最小值的和\(f_i\),不难发现\(f_i=f_{pre_i}+(i-pre_i)\

(莫队算法)CodeForces - 617E XOR and Favorite Number

题意: 长度为n的数列,m次询问,还有一个k.每次询问询问询问从数列的L到R内有多少个连续子序列异或起来等于k. 分析: 因为事先知道这题可以用莫队写,就正好用这题练习莫队. 预处理每个前缀异或和. 然后莫队按分块排序后,不断更新,用一个数组cnt[]记录当前L到R前缀和的数量. R向右拉,新增的数量就是cnt[pre^k],pre表示当前这个R位置的前缀异或和,然后更新一下cnt. 其他的也类似. 算是一个比较好的入门题. 代码: 1 #include <cstdio> 2 #include