FZU-2297 Number theory(数论)(简单线段树)

Given a integers x = 1, you have to apply Q (Q ≤ 100000) operations: Multiply, Divide.

给定一个整数x=1,你需要去运行Q次操作:乘或者除。

Input

First line of the input file contains an integer T(0 < T ≤ 10) that indicates how many cases of inputs are there.

输入的第一行包含一个整数T,意思是指输入的样例个数

The description of each case is given below:

每个样例的描述如下面给定的所示

The first line contains two integers Q and M. The next Q lines contains the operations in ith line following form:

第一行包含两个正数Q和M,接下来的Q行输入包含以下形式的操作。

M yi: x = x * yi.

把x与yi的乘积赋值回x,其中yi为输入值

N di: x = x / ydi.

把x与第di个y的整除商返回赋值给x,其中di为输入值,ydi指的是在乘法操作中输入的第di行的y,

It’s ensure that di is different. That means you can divide yi only once after yi came up.

保证每个di都不同。这意味着你在yi出现之后只用除它一次。

0 < yi ≤ 10^9, M ≤ 10^9

Output

For each operation, print an integer (one per line) x % M.

对于每个操作,输出一个整数(一行内)x%M

Sample Input

1
10 1000000000
M 2
D 1
M 2
M 10
D 3
D 4
M 6
M 7
M 12
D 7

Sample Output

2
1
2
20
10
1
6
42
504
84

我们看到题目中有取余的要求。加法和乘法是适用同余模定理的,但除法不能。所以我们一般把除法转换成“被除数乘以除数的逆元”的乘法形式,但是同只有行列相同的矩阵才有逆矩阵一样,倘若那个“除数”与输入的“MOD(取余数)”不是互质数,那么这个逆元不存在,无法进行运算。所以这里不能用逆元解题。

这个时候,线段树的神秘作用就显现出来了。它的头结点承载的值(根节点-root)总是代表着下边所有节点“作用的总和”,比如说,加法中,root节点就是所有数据的和,最大最小值计算中,root节点承载所有数据的最大最小值,同样,我们把一个数拆成各种因子,放在线段树中,root节点自然有所有数的积,恰好出现的所有数只会被除一次,所以每一次除法就是把一个因数变成1使之失去作用。

那么这个题就有如下的思路:

  1. 准备好线段树建造函数(这里是construction())和线段树单点修改函数(这里是quest_and_modify()),由于一组数据只会有Q行操作,所以说这个线段树至多承载Q个数,所以对1-Q的数据建立线段树即可,初始化各点数据为1。
  2. 设当前输入到第line(i)行(1<=i<=Q)在乘法操作中,每输入一个数就调用单点修改函数在第line(i)个节点中乘上输入的数,此时它就是一个因子,对于除法操作,就是把指定位置的因子变成1。每次操作后直接输出ROOT节点(1号节点)承载的值即可。

细节详见代码:

#include <iostream>
#include <cstdio>
//#include <bits/stdc++.h>
#include <map>
#include <queue>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
struct node
{
    ll left,right,value;
}nodes[1000000];//结构体作节点来建树
ll query,modu;
void construction(ll current,ll left,ll right)//线段树建造函数
{
    nodes[current].left=left,nodes[current].right=right;
    if(left==right)
    {
        nodes[current].value=1;
        return ;
    }
    ll mid=(left+right)>>1;
    construction(2*current,left,mid);
    construction(2*current+1,mid+1,right);
    nodes[current].value=(nodes[2*current].value*nodes[2*current+1].value)%modu;
//注意上式的取余.
}
void quest_and_modi(ll current,ll location,ll number)//线段树单点修改函数
{
    if(nodes[current].left==nodes[current].right)
    {
        nodes[current].value=number;
        return ;
    }
    ll mid=(nodes[current].left+nodes[current].right)>>1;
    if(mid<location)
       quest_and_modi(2*current+1,location,number);
    else
       quest_and_modi(2*current,location,number);
    nodes[current].value=(nodes[2*current].value*nodes[2*current+1].value)%modu;
}
int DETERMINATION()
{
    cin.ios::sync_with_stdio(false);//取消cin与scanf的同步,加快输入(不取消TLE,取消986ms(限时2000ms))
    ll t;
    cin>>t;
    while(t--)
    {
        reset(nodes,0);
        cin>>query>>modu;
        construction(1,1,query);
        //cout<<"!"<<endl;
        for(int i=1;i<=query;i++)
        {
            char com;
            cin>>com;
            ll key;
            cin>>key;
            if(com=='M')
            {
                quest_and_modi(1,i,key);
                cout<<nodes[1].value<<endl;//头节点的值就是题目给定的x当前的值
            }
            else
            {
                quest_and_modi(1,key,1);
                cout<<nodes[1].value<<endl;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43874261/article/details/89533101