noip模拟赛 Nephren Ruq Insania

题目背景

大样例下发链接: https://pan.baidu.com/s/1nuVpRS1 密码: sfxg

注意:本题大样例4的输出文件修改为 https://pan.baidu.com/s/1bUWuZW

奈芙莲·卢可·印萨尼亚(Nephren-Ruq-Insania)

同为妖精仓库的成体妖精兵,天赋不如珂朵莉一般,只是一个平凡的妖精.

睡觉时如同毯子一般在威廉身上为其保暖。习惯于粘着威廉,在梦境中与艾尔梅莉亚交谈时,自称就像是威廉的宠物一样。

本题题面中含有大量的剧透,建议做题之前将这部番剧看完(

题目描述

她只是一个非常普通的黄金妖精。

在援救打捞队的作战中,他们不幸与(几乎是所有的)第六兽相遇了。

此时的珂朵莉因为接触到星神艾露可本体,正处于昏迷之中。而威廉也无法离开珂朵莉。

默默守护在房间外的她,提起圣剑,走向了战场。

作为本身天赋只是一般的妖精少女,她难以对抗无数倍于自己的六号兽。

没有多久,她开始体力不支。

终于,在源源不断的六号兽面前,她难以抵挡了……

终于,由于魔力过度激发,她已经处在了魔力失控的边缘……

”威廉,拯救,是我们黄金妖精的使命。“

”况且,威廉之前已经救过我们了。“

”所以,已经没有问题了。“

威廉想要救下奈芙莲,但是他自己也已经处于崩溃的边缘。

冥冥之中他想起了曾经学习过的一种魔法。在这最后一刻,或许已经是唯一的办法了。

这种魔法操作的对象是一个咒语组成的序列,每一个单独的咒语拥有自己的法力值。

威廉需要不断地按照之前的记忆,对某一段区间的法力值加上一个数,或者求出某一段区间的法咒共鸣。

分析:这道题部分分还是比较多的.第一个数据点看起来数据非常小,但是3^3^3^3^3mod p会算不出来,因为次数很大,不能直接对次数取模.怎么将次数变小呢?欧拉定理! ,可以发现如果这道题就是不断地使用欧拉定理,直到φ(p)变成1或者计算完整个区间,这实际上就是一个递归的过程.用线段树进行区间修改,单点查询。

      有几个地方需要注意:

      1.如果区间[l,r]中第x位是1,那么[x,r]都不需要考虑了,因为1^n = 1.

      2.欧拉定理成立的条件是x >= φ(p),而由于忽略0和1的情况,2^2^2^2^2就足以满足数据范围了,所以暴力枚举5位乘起来看看是不是>=φ(p)就可以了.如果<φ(p)就可以直接算,而不需要欧拉定理了.

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 500010;

int n, m, prime[20000010], phi[20000010], cnt, flag[maxn];
long long tag[maxn << 2], c[maxn << 2], a[maxn], L[maxn << 2], R[maxn << 2];
bool vis[20000010];

void init()
{
    phi[1] = 1;
    for (int i = 2; i <= 20000000; i++)
    {
        if (!vis[i])
        {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= cnt; j++)
        {
            int t = i * prime[j];
            if (t > 20000000)
                break;
            vis[t] = 1;
            if (i % prime[j] == 0)
            {
                phi[t] = phi[i] * prime[j];
                break;
            }
            phi[t] = phi[i] * (prime[j] - 1);
        }
    }
}

void pushup(int o)
{
    c[o] = c[o * 2] + c[o * 2 + 1];
}

void pushdown(int o)
{
    if (tag[o])
    {
        tag[o * 2] += tag[o];
        tag[o * 2 + 1] += tag[o];
        c[o * 2] += (R[o * 2] - L[o * 2] + 1) * tag[o];
        c[o * 2 + 1] += (R[o * 2 + 1] - L[o * 2 + 1] + 1) * tag[o];
    }
    tag[o] = 0;
}

void build(int o, int l, int r)
{
    L[o] = l;
    R[o] = r;
    if (l == r)
    {
        c[o] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(o * 2, l, mid);
    build(o * 2 + 1, mid + 1, r);
    pushup(o);
}

void update(int o, int l, int r, int x, int y, int v)
{
    if (x <= l && r <= y)
    {
        tag[o] += v;
        c[o] += v;
        return;
    }
    pushdown(o);
    int mid = (l + r) >> 1;
    if (x <= mid)
        update(o * 2, l, mid, x, y, v);
    if (y > mid)
        update(o * 2 + 1, mid + 1, r, x, y, v);
    pushup(o);
}

long long query(int o, int l, int r, int pos)
{
    if (l == r)
        return c[o];
    pushdown(o);
    int mid = (l + r) >> 1;
    if (pos <= mid)
        return query(o * 2, l, mid, pos);
    else
        return query(o * 2 + 1, mid + 1, r, pos);
}

long long Cal(int q)
{
    if (flag[q] == m)
        return a[q];
    flag[q] = m;
    return a[q] = query(1, 1, n, q);
}

long long qpow(long long a, long long b, long long mod)
{
    a %= mod;
    long long res = 1;
    while (b)
    {
        if (b & 1)
            res = (res * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res;
}

long long jisuan(int l, int r, int mod)
{
    if (mod == 1)
        return 1;
    if (l == r)
    {
        long long t = Cal(l);
        if (t < mod)  //直接算
            return t % mod;
        else
            return (t % mod) + mod;
    }
    int minn = min(n, l + 5);
    for (int i = l + 1; i <= minn; i++)
        if (Cal(i) == 1)
        {
            minn = i;
            break;
        }
    long long p = Cal(minn), tot = 0;
    for (int i = minn - 1; i >= l + 1; i--)
    {
        tot = p;
        p = 1;
        while (tot--)
        {
            p *= Cal(i);
            if (p >= phi[mod])
                return qpow(Cal(l) % mod, jisuan(l + 1, r, phi[mod]) + phi[mod], mod);
        }
    }
    return qpow(Cal(l) % mod, jisuan(l + 1, r, phi[mod]), mod);
}

int main()
{
    memset(flag, -1, sizeof(flag));
    init();
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    build(1, 1, n);
    while (m--)
    {
        int op, l, r, mod;
        scanf("%d%d%d%d", &op, &l, &r, &mod);
        if (op == 1)
            update(1, 1, n, l, r, mod);
        else
            printf("%lld\n", jisuan(l, r, mod) % mod);
    }

    return 0;
}

 

posted @ 2017-10-21 23:13  zbtrs  阅读(1506)  评论(3编辑  收藏  举报