当前位置: 首页 > article >正文

P4681 [THUSC 2015] 平方运算 Solution

Description

给定序列 a = ( a 1 , a 2 , ⋯   , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,,an) 和常数 p p p ,有 m m m 个操作,分以下两种:

  • modify ⁡ ( l , r ) \operatorname{modify}(l,r) modify(l,r):对每个 i ∈ [ l , r ] i \in [l,r] i[l,r] 执行 a i ← a i 2   m o d   p a_i \leftarrow a_i^2 \bmod p aiai2modp.
  • query ⁡ ( l , r ) \operatorname{query}(l,r) query(l,r):求 ∑ i = l r a i \sum\limits_{i=l}^r a_i i=lrai.

Limitations

1 ≤ n , m ≤ 1 0 5 1 \le n,m \le 10^5 1n,m105
0 ≤ a i < p \textcolor{red}{0} \le a_i < p 0ai<p
p ∈ { 233 , 2332 , 5 , 8192 , 23 , 45 , 37 , 4185 , 5850 , 2975 , 2542 , 2015 , 2003 , 2010 , 4593 , 4562 , 1034 , 5831 , 9905 , 9977 } p \in {\{233,2332,5,8192,23,45,37,4185,5850,2975,2542,2015,2003,2010,4593,4562,1034,5831,9905,9977\}} p{233,2332,5,8192,23,45,37,4185,5850,2975,2542,2015,2003,2010,4593,4562,1034,5831,9905,9977}
2 s , 250 MB 2\text{s},250\text{MB} 2s,250MB

Solution

modify ⁡ \operatorname{modify} modify 操作不能直接标记,也不能直接暴力改.
但是模意义下的平方运算显然是有周期性的,打表发现,在 p p p 取给定数时,所有数的周期的 lcm ⁡ \operatorname{lcm} lcm 不大于 60 60 60,并且每个数平方不超过 11 11 11 次就会进入循环节.
考虑在线段树上维护:

  • c y c l e cycle cycle:这个区间内的数是否全部进入循环节.
  • s u m i sum_i sumi:全部进入循环节的情况下,每个数平方 i i i 次后的和.
  • n o w now now:当前区间的和在循环节的第几个位置.
  • t a g tag tag:标记,表示儿子需要平方几次.

特别地,如果没有全部进入循环节,则用 s u m 0 sum_0 sum0 来记录和.
然后进入循环节的直接跳,没有的就暴力改(因为不超过 11 11 11 次).
注意当一个数进入循环节时,需要将 s u m i sum_i sumi 全部算出.
剩下就没什么了,一开始时把每个数平方的周期长度全算一遍即可.

Code

4.27 KB , 9.45 s , 193.55 MB    (in   total,   C++   20   with   O2) 4.27\text{KB},9.45\text{s},193.55\text{MB}\;\texttt{(in total, C++ 20 with O2)} 4.27KB,9.45s,193.55MB(in total, C++ 20 with O2)

// Problem: P4681 [THUSC 2015] 平方运算
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4681
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

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

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

struct Node {
    int l, r;
    bool cycle;
    int now, tag;
    array<i64, 60> sum;
};
inline int ls(int u) { return 2 * u + 1; }
inline int rs(int u) { return 2 * u + 2; }

struct SegTree {
    vector<Node> tr;
    vector<int> P, vis;
    int M, mod;
    
    inline SegTree() {}
    inline SegTree(const vector<int>& a, int _mod):
                   P(_mod), vis(_mod, -1), M(1), mod(_mod) {
        for (int i = 0; i < mod; i++) get_loop(i);
        for (int i = 0; i < mod; i++)
            if (P[i] != 0) M = lcm(M, P[i]);
        
        const int n = a.size();
        tr.resize(n << 2);
        build(0, 0, n - 1, a);
    }
    
    inline void get_loop(int x) {
        for (int i = 0, y = x; ; i++, y = y * y % mod) {
            if (vis[y] != -1) {
                P[y] = i - vis[y];
                break;
            }
            else vis[y] = i;
        }
        for (int y = x; vis[y] != -1; y = y * y % mod) vis[y] = -1;
    }
    
    inline void upd(int u) {
        if (P[tr[u].sum[0]] != 0) {
			for (int i = 1; i < M; i++) 
			    tr[u].sum[i] = tr[u].sum[i - 1] * tr[u].sum[i - 1] % mod;
			tr[u].now = 0;
			tr[u].cycle = 1;
		}
		else tr[u].now = tr[u].cycle = 0;
    }
    
    inline void apply(int u, int k) {
        tr[u].tag = (tr[u].tag + k) % M;
        tr[u].now = (tr[u].now + k) % M;
    }
    
    inline void pushup(int u) {
        tr[u].cycle = tr[ls(u)].cycle && tr[rs(u)].cycle;
        tr[u].now = 0;
        if (!tr[u].cycle)
            tr[u].sum[0] = tr[ls(u)].sum[tr[ls(u)].now] + tr[rs(u)].sum[tr[rs(u)].now];
        else {
            int nowL = tr[ls(u)].now, nowR = tr[rs(u)].now;
            for (int i = 0; i < M; i++) {
                tr[u].sum[i] = tr[ls(u)].sum[nowL] + tr[rs(u)].sum[nowR];
                nowL = (nowL + 1) % M;
                nowR = (nowR + 1) % M;
            }
        }
    }
    
    inline void pushdown(int u) {
        if (tr[u].tag) {
            apply(ls(u), tr[u].tag);
            apply(rs(u), tr[u].tag);
            tr[u].tag = 0;
        }
    }
    
    inline void build(int u, int l, int r, const vector<int>& a) {
        tr[u].l = l;
        tr[u].r = r;
        if (l == r) {
            tr[u].sum[0] = a[l];
            tr[u].tag = 0;
            return upd(u);
        }
        const int mid = (l + r) >> 1;
        build(ls(u), l, mid, a);
        build(rs(u), mid + 1, r, a);
        pushup(u);
    }
    
    inline void square(int u, int l, int r) {
        if (l <= tr[u].l && tr[u].r <= r && tr[u].cycle) return apply(u, 1);
        if (tr[u].l == tr[u].r) {
            tr[u].sum[0] = tr[u].sum[0] * tr[u].sum[0] % mod;
            return upd(u);
        }
        const int mid = (tr[u].l + tr[u].r) >> 1;
        pushdown(u);
        if (l <= mid) square(ls(u), l, r);
        if (r > mid) square(rs(u), l, r);
        pushup(u);
    }
    
    inline i64 query(int u, int l, int r) {
        if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum[tr[u].now];
        const int mid = (tr[u].l + tr[u].r) >> 1;
        i64 res = 0;
        pushdown(u);
        if (l <= mid) res += query(ls(u), l, r);
        if (r > mid) res += query(rs(u), l, r);
        return res;
    }
};

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m, p;
	scanf("%d %d %d", &n, &m, &p);
	vector<int> a(n);
	for (int i = 0; i < n; i++) scanf("%d", &a[i]);
	SegTree seg(a, p);
	for (int i = 0, op, l, r; i < m; i++) {
	    scanf("%d %d %d", &op, &l, &r);
	    l--, r--;
	    if (op == 1) seg.square(0, l, r);
	    else printf("%lld\n", seg.query(0, l, r));
	}
	
	return 0;
}

http://www.kler.cn/a/524852.html

相关文章:

  • SQL注入漏洞之高阶手法 宽字节注入以及编码解释 以及堆叠注入原理说明
  • Windows11无法打开Windows安全中心主界面
  • 将ollama迁移到其他盘(eg:F盘)
  • android 音视频系列引导
  • 图漾相机——C++语言属性设置
  • Kotlin开发(六):Kotlin 数据类,密封类与枚举类
  • 2025_1_29 C语言学习中关于指针
  • 前端拖拽相关功能详解,一篇文章总结前端关于拖拽的应用场景和实现方式(含源码)
  • 【AI论文】Omni-RGPT:通过标记令牌统一图像和视频的区域级理解
  • 单机伪分布Hadoop详细配置
  • 萌新学 Python 之数值处理函数 round 四舍五入、abs 绝对值、pow 幂次方、divmod 元组商和余数
  • 利用飞书机器人进行 - ArXiv自动化检索推荐
  • Java基础知识总结(二十六)--Arrays
  • SpringBoot中@Valid与@Validated使用场景详解
  • 生成模型:扩散模型(DDPM, DDIM, 条件生成)
  • 2025年01月29日Github流行趋势
  • 【hot100】刷题记录(6)-轮转数组
  • [ASR]faster-whisper报错Could not locate cudnn_ops64_9.dll
  • AI编译器之——为什么大模型需要Relax?
  • 房屋租赁系统如何借助智能化手段提升管理效率与租客体验
  • 剑指 Offer II 008. 和大于等于 target 的最短子数组
  • 【2024年华为OD机试】(A卷,200分)- 查找树中元素 (JavaScriptJava PythonC/C++)
  • 10.3 LangChain实战指南:解锁大模型应用的10大核心场景与架构设计
  • 【C语言练习题】计算16位二进制数所表示的有符号整数
  • 万物皆有联系:驼鸟和布什
  • Github 2025-01-29 C开源项目日报 Top10