[HNOI 2016]大数

Description

题库链接

给你一个长度为 \(n\) ,可含前导零的大数,以及一个质数 \(p\) 。 \(m\) 次询问,每次询问你一个大数的子区间 \([l,r]\) ,求出子区间中有多少个子串为 \(p\) 的倍数。

\(1\leq n,m\leq 100000\)

Solution

记 \(a_i\) 为大数第 \(i\) 位上的数值。

注意到题目是要求 \[\sum_{i=l}^r\sum_{j=i}^r\left[\sum_{k=i}^ja_k\cdot 10^{j-k}\equiv 0\pmod{p}\right]\]

我们可以将其等价变形 \[\Rightarrow\sum_{i=l}^r\sum_{j=i}^r\left[10^j\sum_{k=i}^ja_k\cdot 10^{-k}\equiv 0\pmod{p}\right]\]

由于 \(p\) 是质数,同时在 \(p\neq 2\wedge p\neq 5\) 时, \(\nexists i,10^i\equiv 0\pmod{p}\) ;且对于 \(\forall i,10^{-i}\) 存在逆元。

所以我们先讨论 \(p\neq 2\wedge p\neq 5\) 的情况。

我们不妨记 \(sum_i=\sum\limits_{j=1}^ia_j\cdot10^{-j}\) 在模 \(p\) 意义下的结果。

原式等价于 \[\begin{aligned}\Rightarrow&\sum_{i=l}^r\sum_{j=i}^r\left[\sum_{k=i}^ja_k\cdot 10^{-k}\equiv 0\pmod{p}\right]\\=&\sum_{i=l}^r\sum_{j=i}^r\left[sum_j-sum_i\equiv 0\pmod{p}\right]\\=&\sum_{i=l}^r\sum_{j=i}^r\left[sum_j=sum_i\right]\end{aligned}\]

发现这不就是莫队的板子么,求子区间内有多少数对相等。直接套板子就好了。

然后对于 \(p= 2\vee p= 5\) ,可以特判,讨论比较简单,不再赘述。

Code

注意这个代码是过不了的。但在 luogu 上强行开 O2 过了,就得过且过吧...

但是可以有这些优化:

  1. 优化莫队的计算过程,不用每次都算一遍组合数;
  2. 不用写 \(hash\) 表,直接排序离散化就好了
//It is made by Awson on 2018.2.11
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('\n'))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 200000;
const int MOD = 1e6+7;
void read(int &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}
void print(LL x) {if (x > 9) print(x/10); putchar(x%10+48); }
void write(LL x) {if (x < 0) putchar('-'); print(Abs(x)); }

int p, n, m, sum[N+5], inv, lim;
int tol[MOD+5];
char num[N+5];
LL ans[N+5]; 

namespace cheat2 {
    struct bittree {
        LL c[N+5];
        void add(int x, int val) {for (; x <= n; x += lowbit(x)) c[x] += val; }
        LL query(int x) {
            LL sum = 0;
            for (; x; x -= lowbit(x)) sum += c[x];
            return sum;
        }
    }T;
    int sum[N+5];
    void main() {
        scanf("%s", num+1); n = strlen(num+1);
        for (int i = 1; i <= n; i++)
            if ((num[i]-48)%2 == 0) T.add(i, i), sum[i] = sum[i-1]+1;
            else sum[i] = sum[i-1];
        read(m); int l, r;
        while (m--) {
            read(l), read(r); writeln(T.query(r)-T.query(l-1)-1ll*(sum[r]-sum[l-1])*(l-1));
        }
    }
}
namespace cheat5 {
    struct bittree {
        LL c[N+5];
        void add(int x, int val) {for (; x <= n; x += lowbit(x)) c[x] += val; }
        LL query(int x) {
            LL sum = 0;
            for (; x; x -= lowbit(x)) sum += c[x];
            return sum;
        }
    }T;
    int sum[N+5];
    void main() {
        scanf("%s", num+1); n = strlen(num+1);
        for (int i = 1; i <= n; i++)
            if ((num[i]-48)%5 == 0) T.add(i, i), sum[i] = sum[i-1]+1;
            else sum[i] = sum[i-1];
        read(m); int l, r;
        while (m--) {
            read(l), read(r); writeln(T.query(r)-T.query(l-1)-1ll*(sum[r]-sum[l-1])*(l-1));
        }
    }
}

struct HASH {
    int k[MOD+5];
    void clear() {memset(k, -1, sizeof(k)); }
    void insert(int x) {
        int loc = x%MOD;
        while (true) {
            if (k[loc] == -1 || k[loc] == x) {k[loc] = x; return; }
            ++loc; if (loc >= MOD) loc %= MOD;
        }
    }
    int query(int x) {
        int loc = x%MOD;
        while (true) {
            if (k[loc] == x) {return loc; }
            ++loc; if (loc >= MOD) loc %= MOD;
        }
    }
}mp;
struct tt {
    int l, r, id;
    bool operator < (const tt &b) const {return l/lim == b.l/lim ? r < b.r : l < b.l; }
}a[N+5];
int quick_pow(int a, int b, int p) {
    int ans = 1;
    while (b) {
        if (b&1) ans = 1ll*ans*a%p;
        b >>= 1, a = 1ll*a*a%p;
    }
    return ans;
}
LL C(int n) {return 1ll*n*(n-1)/2;}
void work() {
    read(p); mp.clear();
    if (p == 2) {cheat2::main(); return; }
    if (p == 5) {cheat5::main(); return; }
    inv = quick_pow(10, p-2, p); scanf("%s", num+1); n = strlen(num+1); lim = sqrt(n);
    mp.insert(0);
    for (int i = 1, j = inv; i <= n; i++, j = 1ll*j*inv%p) {
        sum[i] = (sum[i-1]+1ll*(num[i]-48)*j%p)%p;
        mp.insert(sum[i]);
    }
    read(m);
    for (int i = 1; i <= m; i++) {read(a[i].l), --a[i].l, read(a[i].r), a[i].id = i; }
    sort(a+1, a+m+1);
    LL now = 0; int curl = 0, curr = 0; tol[mp.query(0)] = 1;
    for (int i = 1; i <= m; i++) {
        int l = a[i].l, r = a[i].r;
        int id = mp.query(sum[curl]);
        while (curl < l) now -= C(tol[id]), --tol[id], now += C(tol[id]), ++curl, id = mp.query(sum[curl]);
        id = mp.query(sum[curl-1]);
        while (curl > l) --curl, now -= C(tol[id]), ++tol[id], now += C(tol[id]), id = mp.query(sum[curl-1]);
        id = mp.query(sum[curr+1]);
        while (curr < r) ++curr, now -= C(tol[id]), ++tol[id], now += C(tol[id]), id = mp.query(sum[curr+1]);
        id = mp.query(sum[curr]);
        while (curr > r) now -= C(tol[id]), --tol[id], now += C(tol[id]), --curr, id = mp.query(sum[curr]);
        ans[a[i].id] = now;
    }
    for (int i = 1; i <= m; i++) writeln(ans[i]);
}
int main() {
    work(); return 0;
}

原文地址:https://www.cnblogs.com/NaVi-Awson/p/8445686.html

时间: 2024-11-13 07:51:46

[HNOI 2016]大数的相关文章

HNOI 2016 乱做

  4542: [Hnoi2016]大数 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 488  Solved: 182[Submit][Status][Discuss] Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345.小B还有一个素数P.现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也是P 的倍数).例如

HNOI 2016 省队集训日记

第一天 DeepDarkFantasy 从东京出发,不久便到一处驿站,写道:日暮里.  ——鲁迅<藤野先生> 定义一个置换的平方为对1~n的序列做两次该置换得到的序列.已知一个置换的平方,并且这个结果是一个排列,求该置换. 输入第一行一个数n表示排列长度,接下来一行n个数描述排列. 有解则输出一行n个数表示原排列.否则输出一行一个-1. 测试点编号 特征 0~1 n<=10 2~9 n<=1000000 此题有spj. 考试的时候懵逼了,根本没想清就开始乱打. 题解:由题易得每一个

HNOI 2016 地图

[题目描述] Hoshizora Rin是个特别好动的少女. 一天Rin来到了一个遥远的都市.这个都市有N个建筑,编号从1到N,其中市中心编号为1,这个都市有M条双向通行的街道,每条街道连接着两个建筑,其中某些街道首尾相连连接成了一个环.Rin通过长时间的走访,已经清楚了这个都市的两个特点: 从市中心出发可以到达所有的建筑物. 任意一条街道最多存在与一个简单环中. 令Rin心花怒放的是,每个建筑物都会有拉面售卖.拉面有很多不同的种类,但对于Rin而言只有油腻程度的不同,因此我们把油腻程度相同的拉

【BZOJ 4539】【HNOI 2016】树

http://www.lydsy.com/JudgeOnline/problem.php?id=4539 今天测试唯一会做的一道题. 按题目要求,如果暴力的把模板树往大树上仍,最后得到的大树是$O(n^2)$级别的,不能存储,更不能做了. 把模板树往大树上扔的过程我想象成了两个大节点进行连边,每个大节点代表模板树本身或一部分. 这相当于把初始的大树(此时和模板树相同)缩成一个大节点,每次把模板树的一部分缩成一个大节点往大节点构成的大树上连,最后连好的大节点构成的模板树是$O(n)$级别的. 每个

数据结构(树链剖分,堆):HNOI 2016 network

2215. [HNOI2016]网络 ★★★☆   输入文件:network_tenderRun.in   输出文件:network_tenderRun.out   简单对比时间限制:2 s   内存限制:128 MB [题目描述] [输入格式] [输出格式] [样例输入1] 13 23 1 2 1 3 2 4 2 5 3 6 3 7 4 8 4 9 6 10 6 11 7 12 7 13 2 1 0 8 13 3 0 9 12 5 2 9 2 8 2 2 0 10 12 1 2 2 1 3 2

[HNOI 2016]序列

Description 题库链接 给你一个长度为 \(n\) 的序列 \(A\) ,给出 \(q\) 组询问.每次询问 \([l,r]\) ,求该区间内所有的子序列中最小值的和. \(1\leq n,q\leq 100000,|A_i|\leq 10^9\) Solution 考虑把右端点右移时,会产生 \(r-l+1\) 个新的区间,我们可以来统计这 \(r-l+1\) 个区间的最小值和. 记 \(pre_i\) 为从第 \(i\) 位往左走第一个值比 \(A_i\) 小的位置. 显然在 \(

[HNOI 2016]最小公倍数

Description 题库链接 给定一张 \(N\) 个顶点 \(M\) 条边的无向图(顶点编号为 \(1,2,\cdots,n\) ),每条边上带有权值.所有权值都可以分解成 \(2^a\times 3^b\) 的形式. \(q\) 个询问,每次询问给定四个参数 \(u,v,a,b\) ,请你求出是否存在一条顶点 \(u\) 到 \(v\) 之间的路径,使得路径依次经过的边上的权值的最小公倍数为 \(2^a\times 3^b\) . \(1\leq n,q\leq 50000,1\leq

聊一聊前端模板与渲染那些事儿

欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码): https://segmentfault.com/blog/frontenddriver 作为现代应用,ajax的大量使用,使得前端工程师们日常的开发少不了拼装模板,渲染模板.我们今天就来聊聊,拼装与渲染模板的那些事儿. 如果喜欢本文请点击右侧的推荐哦,你的推荐会变为我继续更文的动力 1 页面级的渲染 在刚有web的时候,前端与后端的交互,非常直白,浏览器端发出URL,后端返回一张拼好了的HTML串

使用 Raspberry Pi 上的传感器在 Node.js 中创建一个 IoT Bluemix 应用程序

先决条件 一个IBM Bluemix 帐号,一个 Raspberry Pi 2 或 3,一个 PIR 运动传感器 适用于本文的 Github 存储库 如果您是一位精明的 Bluemix 开发人员,您可能只想看看如何在 node.js 中与 IoT 建立连接,或者只想了解如何从此 github 存储库中拉取我的代码. git clone https://github.com/nicolefinnie/iot-nodejs-tutorial 以下是实现与 IBM IoT 平台连接在一起的 4 个 R