【BZOJ】4542: [Hnoi2016]大数

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4542



给定一个由数字构成的字符串${S_{1,2,3,...,n}}$,一个正素数$P$,每次询问给定一对$l$,$r$求:

$${\sum_{l=1}^{n}\sum_{r=i}^{n}\left [ \sum _{i=l}^{r}S[i]*10^{r-i} \,\,\,\,MOD\,\,\,\,P=0 \right ]}$$



  即以位置$x$开头的后缀的数字$%P$之后的值为$val[x]$,如果一个数字对应区间${[l,r]}$它$%p$为$0$的充要条件为${val[l]=val[r-1]}$,然后套上莫队算法,离散化$val$数组,就变成了经典的查询一个区间相同数字的点对有多少个。

  注意:$p=2,5$的情况中并不满足以上性质,记一下前缀和然后特判即可。


  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<cstdlib>
  6 #include<cmath>
  7 #include<cstring>
  8 using namespace std;
  9 #define maxn 300100
 10 #define llg long long
 11 #define yyj(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
 12 llg n,m,p,quan[maxn],tail,val[maxn],len,se[maxn],KUAI;
 13 long long ans,Ans[maxn],jisuan1[maxn],jisuan2[maxn];
 14 char s[maxn];
 15 struct Q_
 16 {
 17     llg l,r,num;
 18 }ask[maxn];
 19
 20 bool cmp(const Q_&a,const Q_&b) {if (a.l/KUAI==b.l/KUAI) return a.r<b.r; else return a.l/KUAI<b.l/KUAI; }
 21
 22 llg find(llg x)
 23 {
 24     llg l=1,r=len,wz=-1,mid;
 25     while (l<=r)
 26     {
 27         mid=(l+r)>>1;
 28         if (quan[mid]<=x) {wz=mid; l=mid+1;}else {r=mid-1;}
 29     }
 30     return wz;
 31 }
 32
 33 void solve()
 34 {
 35     cin>>m;
 36     for(int i=1;i<=n;i++)
 37     {
 38         jisuan1[i]=jisuan1[i-1]; jisuan2[i]=jisuan2[i-1];
 39         if( (s[i]-‘0‘)%p==0 )
 40              {
 41             jisuan1[i]++;
 42             jisuan2[i]+=i;
 43              }
 44
 45     }
 46     for(int i=1;i<=m;i++)
 47     {
 48         llg x,y;
 49         scanf("%lld%lld",&x,&y);
 50         printf("%lld\n",jisuan2[y]-jisuan2[x-1] - (x-1)*(jisuan1[y]-jisuan1[x-1]));
 51     }
 52 }
 53
 54 void init()
 55 {
 56     cin>>p;
 57     scanf("%s",s+1);
 58     n=strlen(s+1);
 59     KUAI=sqrt(n)+1;
 60     if (p==5 || p==2) return ;
 61     cin>>m;
 62     for (llg i=1;i<=m;i++) scanf("%lld%lld",&ask[i].l,&ask[i].r),ask[i].r++,ask[i].num=i;
 63     sort(ask+1,ask+m+1,cmp);
 64     llg C=1;
 65     tail=1;quan[1]=0;
 66     for (llg i=n;i>=1;i--)
 67     {
 68         val[i]=val[i+1]+C*(s[i]-‘0‘);
 69         val[i]%=p;
 70         quan[++tail]=val[i];
 71         C*=10; C%=p;
 72     }
 73     sort(quan+1,quan+tail+1);
 74     len=unique(quan+1,quan+tail+1)-(quan+1);
 75     for (llg i=1;i<=n+1;i++) val[i]=find(val[i]);
 76 }
 77
 78 int main()
 79 {
 80     yyj("number");
 81     init();
 82     if (p==2 || p==5) {solve(); return 0;}
 83
 84     llg l=ask[1].l,r=ask[1].r;
 85     for (llg i=l;i<=r;i++)
 86     {
 87         ans+=se[val[i]];
 88         se[val[i]]++;
 89     }
 90     Ans[ask[1].num]=ans;
 91     for (llg k=2;k<=m;k++)
 92     {
 93         llg nl=ask[k].l,nr=ask[k].r;
 94         if (nr>r)
 95         {
 96             for (llg i=r+1;i<=nr;i++)
 97             {
 98                 ans+=se[val[i]];
 99                 se[val[i]]++;
100             }
101         }
102         if (nr<r)
103         {
104             for (llg i=r;i>nr;i--)
105             {
106                 ans-=se[val[i]]-1;
107                 se[val[i]]--;
108             }
109         }
110         if (l<nl)
111         {
112             for (llg i=l;i<nl;i++)
113             {
114                 ans-=se[val[i]]-1;
115                 se[val[i]]--;
116             }
117         }
118         if (l>nl)
119         {
120             for (llg i=l-1;i>=nl;i--)
121             {
122                 ans+=se[val[i]];
123                 se[val[i]]++;
124             }
125         }
126         Ans[ask[k].num]=ans;
127         l=nl,r=nr;
128     }
129     for (llg i=1;i<=m;i++) printf("%lld\n",Ans[i]);
130     return 0;
131 }
时间: 2024-10-08 00:02:59

【BZOJ】4542: [Hnoi2016]大数的相关文章

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

4542: [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】[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

大数(bzoj 4542)

/* 想了半天莫队,不知道咋转移,需要动下脑子. 有一个很显然的结论是如果(l,r)是P的倍数,那么s[l...n]%P=s[r+1...n]%P. 根据这个东西,我们预处理出所有的后缀%P的余数,接下里就是查询区间内相同得数对数量,就很好转移了. 有一点,当P=2或5时,不否和上面的情况,需单独讨论. */ #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #

BZOJ 4541: [Hnoi2016]矿区 平面图转对偶图+DFS树

4541: [Hnoi2016]矿区 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 433  Solved: 182[Submit][Status][Discuss] Description 平面上的矿区划分成了若干个开发区域.简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若 干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点 的线段组成.每个开发区域的矿量与该开发区域的面积有关:具

BZOJ 4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 400[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公

BZOJ 3110 K大数查询 树套树

题意:链接 方法:树套树(线段树套线段树) 题解:这题好神啊- -自己在做的时候一顿yy也没yy出用两个线段树来搞,其实我想的是类似二逼那道题那样,用线段树维护总区间,treap维护每个节点,不过这样的话,尼玛修改就是暴力有没有?而且查询的时候也是暴力啊有没有?绝壁不是这么做的啊! 上网上找了找题解综合了大家的思想自己也是懂了这题是咋回事了,也是跪了. 好不扯淡了,谈正经的,两个线段树是怎么搞得. 其实第一棵线段树是区间的线段树,而第二棵是维护值域的线段树,具体解析请看代码. #include

【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的倍数.于是莫队一下就好了. 代码: #

BZOJ4542 [HNOI2016] 大数

[问题描述] 小 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的倍数. [输入格式] 第一行一个整数:P.第二行一个串:S.第三行一个整数:M.接下来M行,每行两个整数