NC17383 A Simple Problem with Integers

Posted 空白菌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NC17383 A Simple Problem with Integers相关的知识,希望对你有一定的参考价值。

题目链接

题目

题目描述

You have N integers A1, A2, ... , AN. You are asked to write a program to receive and execute two kinds of instructions:

  1. C a b means performing \\(A_i = A_i^2 \\mod 2018\\) for all Ai such that a ≤ i ≤ b.
  2. Q a b means query the sum of Aa, Aa+1, ..., Ab. Note that the sum is not taken modulo 2018.

输入描述

The first line of the input is T(1≤ T ≤ 20), which stands for the number of test cases you need to solve.
The first line of each test case contains N (1 ≤ N ≤ 50000).The second line contains N numbers, the initial values of A1, A2, ..., An. 0 ≤ Ai < 2018. The third line contains the number of operations Q (0 ≤ Q ≤ 50000). The following Q lines represents an operation having the format "C a b" or "Q a b", which has been described above. 1 ≤ a ≤ b ≤ N.

输出描述

For each test case, print a line "Case #t:" (without quotes, t means the index of the test case) at the beginning.
You need to answer all Q commands in order. One answer in a line.

示例1

输入

1
8
17 239 17 239 50 234 478 43
10
Q 2 6
C 2 7
C 3 4
Q 4 7
C 5 8
Q 6 7
C 1 8
Q 2 5
Q 3 4
Q 1 8

输出

Case #1:
779
2507
952
6749
3486
9937

题解

知识点:线段树,数论。

显然,区间同余是没法直接运用懒标记的,我们需要找到能使用懒标记的结构。

容易证明, \\(A_i = A_i^2 \\mod 2018\\) 运算在有限次操作后一定会进入一个循环节,且长度的最小公倍数不超过 \\(6\\) 。而且可以发现,进入循环的需要的操作次数其实很少。

注意到,进入循环的区间可以预处理出所在循环节的所有值,并用一个指针指向当前值即可,随后每次修改相当于在环上移动指针,显然可以懒标记。对于未进入循环节的区间,先采用单点修改,直到其值进入循环节。

因此,我们先预处理枚举 \\([0,2018)\\) 所有数是否在循环节内,用 \\(cyc\\) 数组记录每个数的所在循环节的长度。如果某数的循环节长度非 \\(0\\) ,则其为循环节的一部分。我们对循环节长度取最小公倍数 \\(cyclcm\\),以便统一管理。

对此,区间信息需要维护区间是否进入循环 \\(lp\\) 、区间循环值 \\(sum\\) 数组、区间值指针 \\(pos\\) 。若未进入循环,则值存 \\(sum[0]\\) ,且 \\(pos = 0\\) ;若进入循环,则 \\(sum\\) 存循环的各个值, \\(pos\\) 指向当前值的位置。

区间合并,有两种情况:

  1. 存在子区间未进入循环,则区间未进入循环,最终值由子区间当前值相加。
  2. 子区间都进入循环,则区间进入循环,顺序遍历子区间对应的循环值,并将和更新到区间的 \\(sum\\)

区间修改只需要维护指针平移总量 \\(cnt\\)

区间修改,有两种情况:

  1. 区间未进入循环,则继续递归子区间,直到单点修改。每次单点修改后,检查是否进入循环,若进入循环,则预处理出 \\(sum\\)
  2. 区间已进入循环,则直接平移 \\(pos\\) 即可。

标记修改,直接加到标记上即可,可以模 \\(2018\\)。注意,当且仅当区间进入循环后标记才有意义,但事实上,未进入循环的区间标签始终为 \\(0\\) ,对修改没有影响,无需特判。

这道题实际上是洛谷P4681的弱化版,我这里使用了通解的做法,比只针对这道题的做法要慢一点。只针对这道题的做法是基于另一个更进一步的结论,所有数字在 \\(6\\) 次操作之后一定进入循环,那么只需要记录一个单点是否操作 \\(6\\) 次作为检查条件即可,省去了枚举 \\([0,2018)\\) 所有数字的循环节长度的时间。

时间复杂度 \\(O((n+m) \\ log n)\\)

空间复杂度 \\(O(n)\\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int P = 2018;
int cyc[P];
int cyclcm;

void init_cyc() 
    cyclcm = 1;
    for (int i = 0;i < P;i++) 
        cyc[i] = 0;
        vector<int> vis(P);
        for (int j = 1, x = i;;j++, x = x * x % P) 
            if (vis[x]) 
                if (x == i) 
                    cyc[i] = j - vis[i];
                    cyclcm = lcm(cyclcm, cyc[i]);
                
                break;
            
            vis[x] = j;
        
    


class SegmentTreeLazy 
    struct T 
        array<int, 6> sum;
        int pos;
        bool lp;
    ;
    struct F 
        int cnt;
    ;
    int n;
    vector<T> node;
    vector<F> lazy;

    void push_up(int rt) 
        node[rt].lp = node[rt << 1].lp && node[rt << 1 | 1].lp;
        node[rt].pos = 0;
        if (node[rt].lp)
            for (int i = 0, l = node[rt << 1].pos, r = node[rt << 1 | 1].pos;
                i < cyclcm;
                i++, ++l %= cyclcm, ++r %= cyclcm)
                node[rt].sum[i] = node[rt << 1].sum[l] + node[rt << 1 | 1].sum[r];
        else node[rt].sum[0] = node[rt << 1].sum[node[rt << 1].pos] + node[rt << 1 | 1].sum[node[rt << 1 | 1].pos];
    

    void push_down(int rt) 
        (node[rt << 1].pos += lazy[rt].cnt) %= cyclcm;
        (lazy[rt << 1].cnt += lazy[rt].cnt) %= cyclcm;
        (node[rt << 1 | 1].pos += lazy[rt].cnt) %= cyclcm;
        (lazy[rt << 1 | 1].cnt += lazy[rt].cnt) %= cyclcm;
        lazy[rt].cnt = 0;
    

    void check(int rt) 
        node[rt].pos = 0;
        if (cyc[node[rt].sum[0]]) 
            node[rt].lp = 1;
            for (int i = 1;i < cyclcm;i++)
                node[rt].sum[i] = node[rt].sum[i - 1] * node[rt].sum[i - 1] % P;
        
        else node[rt].lp = 0;
    

    void update(int rt, int l, int r, int x, int y) 
        if (r < x || y < l) return;
        if (x <= l && r <= y && node[rt].lp) 
            ++node[rt].pos %= cyclcm;
            ++lazy[rt].cnt %= cyclcm;
            return;
        
        if (l == r) 
            node[rt].sum[0] = node[rt].sum[0] * node[rt].sum[0] % P;
            check(rt);
            return;
        
        push_down(rt);
        int mid = l + r >> 1;
        update(rt << 1, l, mid, x, y);
        update(rt << 1 | 1, mid + 1, r, x, y);
        push_up(rt);
    

    int query(int rt, int l, int r, int x, int y) 
        if (r < x || y < l) return 0;
        if (x <= l && r <= y) return node[rt].sum[node[rt].pos];
        push_down(rt);
        int mid = l + r >> 1;
        return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
    

public:
    SegmentTreeLazy(const vector<int> &src)  init(src); 

    void init(const vector<int> &src) 
        assert(src.size() >= 2);
        n = src.size() - 1;
        node.assign(n << 2,  ,0,0 );
        lazy.assign(n << 2,  0 );
        function<void(int, int, int)> build = [&](int rt, int l, int r) 
            if (l == r) 
                node[rt].sum[0] = src[l];
                check(rt);
                return;
            
            int mid = l + r >> 1;
            build(rt << 1, l, mid);
            build(rt << 1 | 1, mid + 1, r);
            push_up(rt);
        ;
        build(1, 1, n);
    

    void update(int x, int y)  update(1, 1, n, x, y); 

    int query(int x, int y)  return query(1, 1, n, x, y); 
;
//* 朴素操作开销太大(array复制),因此全部展开

bool solve() 
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1;i <= n;i++) cin >> a[i];

    init_cyc();
    SegmentTreeLazy sgt(a);

    int m;
    cin >> m;
    while (m--) 
        char op;
        int l, r;
        cin >> op >> l >> r;
        if (op == \'C\') sgt.update(l, r);
        else cout << sgt.query(l, r) << \'\\n\';
    
    return true;


int main() 
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    for (int i = 1;i <= t;i++) 
        cout << "Case #" << i << ":" << \'\\n\';
        if (!solve()) cout << -1 << \'\\n\';
    
    return 0;

HDU 1757 A Simple Math Problem

Description

Lele now is thinking about a simple function f(x). 

If x < 10 f(x) = x. 
If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10); 
And ai(0<=i<=9) can only be 0 or 1 . 

Now, I will give a0 ~ a9 and two positive integers k and m ,and could you help Lele to caculate f(k)%m. 

Input

The problem contains mutiple test cases.Please process to the end of file. 
In each case, there will be two lines. 
In the first line , there are two positive integers k and m. ( k<2*10^9 , m < 10^5 ) 
In the second line , there are ten integers represent a0 ~ a9. 

Output

For each case, output f(k) % m in one line.
 

Sample Input

10 9999
1 1 1 1 1 1 1 1 1 1
20 500
1 0 1 0 1 0 1 0 1 0
 

Sample Output

45
104
 

题意:按f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10) (x>=10) ; f(x) = x(x<10)来计算f(x)%m的值。

分析:这题要用递推,并且k值很大,所以需要用矩阵快速幂。

构造的矩阵是:

a0 a1 a2 a3 a4 a5 a6 a7 a8 a9
1                  
  1                
    1              
      1            
        1          
          1        
            1      
              1    
                1  
*
f(x-1)
f(x-2)
f(x-3)
f(x-4)
f(x-5)
f(x-6)
f(x-7)
f(x-8)
f(x-9)
f(x-10)
=
f(x)
f(x-1)
f(x-2)
f(x-3)
f(x-4)
f(x-5)
f(x-6)
f(x-7)
f(x-8)
f(x-9)

写个结构类型代表矩阵,以及矩阵的相乘的函数和矩阵快速幂的函数,注意一下初始化。

#include<stdio.h>
#include<string.h>
int n,k,m;
struct matrix
{
    int a[15][15];
    int row,col;
    void init(int r,int c){
        memset(a,0,sizeof(a));
        row=r;col=c;
    }
} big,f,u;
matrix mul(matrix a,matrix b)
{
    matrix c;
    c.init(a.row,b.col);
    for(int i=0; i<a.row; i++)
        for(int j=0; j<b.col; j++)
        {
            for(int k=0; k<a.col; k++)
                c.a[i][j]+=(a.a[i][k]*b.a[k][j])%m;
            c.a[i][j]%=m;
        }
    return c;
}
void init()
{
    big.init(10,10);
    f.init(10,1);
    for(int i=1; i<10; i++)
        big.a[i][i-1]=1;
    for(int i=0; i<10; i++)
        f.a[i][0]=9-i;
}
matrix qpow(matrix a,int k)
{
    matrix ans;
    ans.init(a.row,a.col);
    for(int i=0;i<a.row;i++)
        ans.a[i][i]=1;
    while(k)
    {
        if(k&1)ans=mul(ans,a);
        a=mul(a,a);
        k>>=1;
    }
    return ans;
}
int main()
{
    init();
    while(~scanf("%d%d",&k,&m))
    {
        for(int i=0; i<10; i++)
            scanf("%d",&big.a[0][i]);
        u=mul(qpow(big,k-9),f);
        printf("%d\n",u.a[0][0]%m);
    }
    return 0;
}

以上是关于NC17383 A Simple Problem with Integers的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 3489: A simple rmq problem

HDU 1757 A Simple Math Problem

A Simple Problem

A Simple Problem

poj 3466 A Simple Problem with Integers

A Simple Problem with Integers