CCF计算机软件能力认证试题练习:201912-5 魔数

前置知识:BFS,线段树等

\(f(x) = (x\%A)\%B\)

这个函数值的和直接用线段树维护是不太行的(也可能是我不知道),后来想了很久的取模技巧操作...但是越想越远根本不着边际

网上也找不到题解,就去大佬群里面求助,cls一两句话就解决了这道题...

这几个U随便乘,最后只会有不到40个本质不同的数字

关于这个结论是如何得到的,不清楚,但是是很容易验证的,代码如下:

由于数字刚好不超过无符号longlong的范围,所以可以用快速乘来解决(复杂度log),算法类似于快速幂,如果没有了解过的同学可以去学一下

ull U[5] = {
314882150829468584,
427197303358170108,
1022292690726729920,
1698479428772363217,
2006101093849356424
};
ull mod = 2009731336725594113;
unordered_map<ull, int> mp;
ull f[35],a[N][35];
int g[35][35];
ull mul(ull a, ull b){
ull res = 0;
for(;b;b>>=1){
if(b & 1) res = (res + a) % mod;
a = (a + a) % mod;
}
return res;
}
void getConvert(){
queue<ull> q;
int id = 1;
for(int i=0;i<5;i++)q.push(U[i]), mp[U[i]] = id++;
//BFS找到所有可能的结果
while(q.size()){
ull x = q.front();q.pop();
f[mp[x]] = x;
for(int i=0;i<5;i++){
ull t = mul(x, U[i]);
if(mp[t]) {
continue;
}
mp[t] = id++;
q.push(t);
}
}
// 得到转移函数
for(int i=1;i<=32;i++){
for(int j=i;j<=32;j++){
g[i][j] = g[j][i] = mp[mul(f[i], f[j])];
}
}
}

然后该怎么解决?线段树维护区间乘这32种数字的结果和。

因为不管怎么乘,最终结果只会对应乘的是这32个数字中的某一个。

对于线段树上的某个节点,\(s[i]\) 表示区间内每个数乘了第 i 个数字即 \(f[i]\) 后模2019的和。

对于每次修改,假如要乘第 \(f[i]\) 个数字,那么对于 \(j \in [1,32], s[j] = s[g[j][i]]\) g数组的含义见上述代码,也就是 本来这个区间都乘了 \(f[j]\),然后又乘了个 \(f[i]\),那么最终就会发生转移,相当于乘第 \(g[j][i]\) 个数字,对应于原来的 \(s[g[j][i]]\)

然后区间要打标记,如果之前没有标记过,那么 \(tag = i\) 表示当前乘了第 i 个数字,否则\(tag = g[tag][i]\)进行转移

线段树上面的具体操作就是这样,但是我兴高采烈的写完就交了上去,一直T到怀疑人生。

初始化线段树节点只有 2n 个,每个初始化32次,复杂度根本不会炸

每次查询是 \(32\log n\) ,总共1e5次查询,也不会炸,那是为什么呢?

实在没办法了,输入了个1000000 100000试了试...发现大概用了二十秒才初始化结束。突然顿悟原来这里还有个log的操作在做鬼

void build(int p, int l, int r){
t[p].l = l;t[p].r = r;
if(l == r){
for(int i=1;i<=32;i++){
//这一句复杂度是(log2e18),大概是60
//即使反过来换成mul(f[i],l),也会有20左右的大小,根本无法承受
t[p].s[i] = mul(l, f[i]) % 2019;
}
t[p].res = t[p].s[28];
t[p].tag = 0;
return;
}
int mid = l + r >> 1;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
for(int i=1;i<=32;i++){
t[p].s[i] = t[p*2].s[i] + t[p*2+1].s[i];
}
t[p].res = t[p].s[28];
t[p].tag = 0;
}

好了,这次应该知道怎么优化了,题目数据给出 A 序列初值为 1...n 也是有道理的,否则这里的复杂度怎么也优化不掉的

总时间复杂度\(O(32n+32q\log n\))

AC截图

这个时限(4s),空间(1G),感觉真是刚刚好

下面是AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
typedef unsigned long long ull;
const int N = 1000010;
ull U[5] = {
314882150829468584,
427197303358170108,
1022292690726729920,
1698479428772363217,
2006101093849356424
};
ull mod = 2009731336725594113;
unordered_map<ull, int> mp;
ull f[35],a[N][35];
int g[35][35];
ull mul(ull a, ull b){
ull res = 0;
for(;b;b>>=1){
if(b & 1) res = (res + a) % mod;
a = (a + a) % mod;
}
return res;
}
void getConvert(){
queue<ull> q;
int id = 1;
for(int i=0;i<5;i++)q.push(U[i]), mp[U[i]] = id++;
while(q.size()){
ull x = q.front();q.pop();
f[mp[x]] = x;
for(int i=0;i<5;i++){
ull t = mul(x, U[i]);
if(mp[t]) {
continue;
}
mp[t] = id++;
q.push(t);
}
}
for(int i=1;i<=32;i++){
for(int j=i;j<=32;j++){
g[i][j] = g[j][i] = mp[mul(f[i], f[j])];
}
}
}
int tmp[33];
int n, q;
struct SegTree{
int l,r;
int s[33];//维护它,但是它后期可能还会变
int res;
int tag;
// 转移函数,target表示乘 f[target]
void convert(int target){
for(int i=1;i<=32;i++){
tmp[i] = s[g[i][target]];
}
for(int i=1;i<=32;i++){
s[i] = tmp[i];
}
res = s[28];
if(tag == 0) tag = target;
else tag = g[tag][target];
}
}t[N<<2];
/*
下面初始res取 s[28]的原因是32个数字中第28个是 1
*/
void build(int p, int l, int r){
t[p].l = l;t[p].r = r;
if(l == r){
for(int i=1;i<=32;i++){
t[p].s[i] = a[l][i] % 2019;
}
t[p].res = t[p].s[28];
t[p].tag = 0;
return;
}
int mid = l + r >> 1;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
for(int i=1;i<=32;i++){
t[p].s[i] = t[p*2].s[i] + t[p*2+1].s[i];
}
t[p].res = t[p].s[28];
t[p].tag = 0;
}
void pushdown(int p){
if(t[p].tag){
t[2*p].convert(t[p].tag);
t[2*p+1].convert(t[p].tag);
t[p].tag = 0;
}
}
int ask(int p, int l, int r){
if(t[p].l >= l && t[p].r <= r){
return t[p].res;
}
pushdown(p);
int mid = t[p].l + t[p].r >> 1;
int res = 0;
if(mid >= l) res = ask(p*2, l, r);
if(mid < r) res = res + ask(p*2+1, l, r);
return res;
}
void change(int p, int l, int r, int k){
if(t[p].l >= l && t[p].r <= r){
t[p].convert(k+1);
return;
}
pushdown(p);
int mid = t[p].l + t[p].r >> 1;
if(mid >= l) change(p*2, l, r, k);
if(mid < r) change(p*2+1, l, r, k);
for(int i=1;i<=32;i++) t[p].s[i] = t[p*2].s[i] + t[p*2+1].s[i];
t[p].res = t[p].s[28];
}
int main(){
getConvert();
scanf("%d%d", &n, &q);
// 由于序列是1...n,所以可以直接用加法递推
for(int i=1;i<=n;i++){
for(int j=1;j<=32;j++){
if(i == 1) a[i][j] = f[j] % mod;
else a[i][j] = (f[j] + a[i-1][j]) % mod;
}
}
build(1, 1, n);
while(q--){
int l, r;
scanf("%d%d", &l, &r);
int res = ask(1, l, r);
printf("%d\n", res);
change(1, l, r, res % 5);
}
return 0;
}

最后膜一下500分的那位大佬,在没有oj的情况下可以一发AC实在是非人哉

感谢耐心指导我的cls(杭电WF选手)

附样例2输入数据

100 100
45 74
38 50
7 45
42 62
83 100
50 51
8 11
93 98
64 70
15 87
30 87
13 79
14 81
18 79
70 88
25 39
13 57
55 85
80 92
83 90
54 75
1 61
17 42
25 49
39 77
32 45
83 87
30 47
59 84
25 50
1 82
21 45
72 96
3 85
16 64
52 92
28 29
84 88
26 93
10 67
27 76
57 62
43 69
63 66
5 59
9 46
49 53
35 50
3 19
23 62
38 73
17 68
34 83
42 91
13 92
19 62
17 70
18 75
95 99
35 90
81 91
59 63
5 90
22 87
51 88
25 61
56 91
50 78
11 60
11 18
27 45
57 82
16 54
3 94
33 56
9 71
68 88
24 36
7 64
48 85
58 76
20 43
9 90
24 27
71 97
25 95
73 97
55 83
22 43
53 55
68 88
12 44
25 87
14 46
34 56
15 35
7 80
46 87
23 71
88 93

CCF计算机软件能力认证试题练习:201912-5 魔数的更多相关文章

  1. 【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

    这次的题总体上相对前三次偏简单.由于实力有限,就分析前四题.     试题编号:    201503-1 试题名称:    图像旋转 时间限制:    5.0s 内存限制:    256.0MB 问题 ...

  2. 第四届CCF软件能力认证(CSP2015) 第五题(最小花费)题解

    [问题描述] C国共有$n$个城市.有$n-1$条双向道路,每条道路连接两个城市,任意两个城市之间能互相到达.小R来到C国旅行,他共规划了$m$条旅行的路线, 第$i$条旅行路线的起点是$s_i$,终 ...

  3. 第四届CCF软件能力认证

    1.图像旋转 问题描述 旋转是图像处理的基本操作,在这个问题中,你需要将一个图像逆时针旋转90度. 计算机中的图像表示可以用一个矩阵来表示,为了旋转一个图像,只需要将对应的矩阵旋转即可. 输入格式 输 ...

  4. 第三届CCF软件能力认证

    1.门禁系统 问题描述 涛涛最近要负责图书馆的管理工作,需要记录下每天读者的到访情况.每位读者有一个编号,每条记录用读者的编号来表示.给出读者的来访记录,请问每一条记录中的读者是第几次出现. 输入格式 ...

  5. 第二届CCF软件能力认证

    1. 相邻数对 问题描述 给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1. 输入格式 输入的第一行包含一个整数n,表示给定整数的个数. 第二行包含所给定的n个整数. 输出格式 输出一个 ...

  6. 第一届CCF软件能力认证

    1.相反数 问题描述 有 N 个非零且各不相同的整数.请你编一个程序求出它们中有多少对相反数(a 和 -a 为一对相反数). 输入格式 第一行包含一个正整数 N.(1 ≤ N ≤ 500). 第二行为 ...

  7. 第六届CCF软件能力认证

    1.数位之和 问题描述 给定一个十进制整数n,输出n的各位数字之和. 输入格式 输入一个整数n. 输出格式 输出一个整数,表示答案. 样例输入 20151220 样例输出 13 样例说明 201512 ...

  8. 第五届CCF软件能力认证

    1.数列分段 问题描述 给定一个整数数列,数列中连续相同的最长整数序列算成一段,问数列中共有多少段? 输入格式 输入的第一行包含一个整数n,表示数列中整数的个数. 第二行包含n个整数a1, a2, … ...

  9. C1 能力认证——Web进阶

    C1 能力认证--Web进阶 DOM节点操作-上 名称 描述 getElementById() 获取带有指定id的节点 getElementsByTagName() 获取带有指定标签名的节点集合 qu ...

随机推荐

  1. CS系统中分页控件的制作

    需求:在一个已有的CS项目(ERP中),给所有的列表加上分页功能. 分页的几个概念: 总记录数  totalCount (只有知道了总记录数,才知道有多少页) 每页记录数  pageSize (根据总 ...

  2. Lesson_strange_words5

    certain 一些 the company's 公司的 implement 实现 plane 平面 sluggishly 迟缓地,缓慢地 frustrated 懊恼的 profiler 分析器,分析 ...

  3. lcobucci/jwt的安装和使用

    安装 composer require lcobucci/jwt 3.3 封装 <?php namespace tools\jwt; use Lcobucci\JWT\Builder; use ...

  4. Spring Cloud实战 | 第十篇 :Spring Cloud + Seata 1.4.1 + Nacos1.4.0 整合实现微服务架构中逃不掉的话题分布式事务

    Seata分布式事务在线体验地址:https://www.youlai.store 本篇完整源码地址:https://github.com/hxrui/youlai-mall 有想加入开源项目开发的童 ...

  5. selenium自动化 | 实现抢课功能

    # -*- coding: utf-8 -*-"""Created on Wed Jan 1 23:39:34 2020@author: billie程序运行环境要求:- ...

  6. pidof

    pidof 服务名称,就可以查看到服务占用的进程号

  7. 【Docker】1、 前后端分离项目 下载启动运行

    人人开源前后端分离项目下载与配置 文章目录 人人开源前后端分离项目下载与配置 前后端分离框架介绍 后端项目下载与配置 1.renren-fast后台项目介绍 2.开发环境搭建 3.下载后端renren ...

  8. explain extended;show warnings

    mysql> explain extended select count(*) from xuehao;+----+-------------+-------+------+---------- ...

  9. 单片机—Arduino UNO-R3—学习笔记001

    连接方法 下载Arduino软件 安装完成打开如图所示 观察右下角的连接接口"Arduino Uno在COM(X)" 在工具-->端口-->选择之前查看的端口 即为连接 ...

  10. luoguP2016 战略游戏

    题目描述 Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了 ...