NOIP模拟测试A3 赛后总结
T1 谜之阶乘
可以发现题目要求我们求的实际上是若干个连续整数 \(c_i\) ,使得 \(\displaystyle \prod c_i = n\),通过打表可以发现这些连续整数的长度 \(d\) 很小,毕竟 \(20~! > 10^{20}\) ,也就是说我们的 \(d \le 20\),所以我们可以去枚举 \(d\)。可以看出指定 \(d\) 之后,这一串数的值一定在 \(\displaystyle \sqrt[d]{n}\) 附近,直接暴力判断即可。
Code
//A - calc
#include<bits/stdc++.h>
typedef long long valueType;
constexpr valueType maxK = 21;
valueType check(valueType N, valueType k);
void solve(valueType N);
int main() {
int T;
std::cin >> T;
for(int i = 1; i <= T; ++i) {
valueType N;
std::cin >> N;
solve(N);
}
std::cout << std::flush;
return 0;
}
void solve(valueType N) {
std::vector<std::pair<valueType, valueType>> ans;
if(N == 1) {
std::cout << -1 << '\n';
return;
}
ans.emplace_back(N, N - 1);
for(int k = 2; k <= maxK; ++k) {
valueType const result = check(N, k);
if(result != -1)
ans.emplace_back(result + k - 1, result - 1);
}
std::sort(ans.begin(), ans.end());
std::cout << ans.size() << '\n';
for(auto const &iter : ans)
std::cout << iter.first << ' ' << iter.second << '\n';
}
valueType check(valueType N, valueType k) {
valueType const a = std::floor(std::pow<long double>((long double)N, (long double)1 / k));
for(valueType start = std::max(a - k - 1, (valueType)2); start <= a; ++start) {
valueType result = 1, i = start;
for(i = start; i < start + k && result < N; ++i)
result *= i;
if(result == N && i == start + k)
return start;
}
return -1;
}
T2 子集
构造题,首先特判掉 \(k = 1\) 和 \(k = n\) 的情况,然后如果 \(\displaystyle k \nmid \sum_{i = 1}^ni\) 的话,也判无解。
设 \(\displaystyle m = \frac{n}{k}\),如果 \(m\) 为偶数,那么我们可以让从 \(1\) 到 \(n\) 这 \(n\) 个数中 \(i\) 和 \(n - i\) 两两配对放到一个子集中,这样可以保证每个子集的和都是一定的。
我们接下来考虑 \(m\) 为奇数的情况,我们可以把 \(m\) 分解为 \(m = 2r + 3\) 对于每个子集我们特殊处理 \(3\) 组数,剩余的 \(2r\) 组按照上面说的处理。
我们希望 这 \(3\) 组数的和相等,我们将其标号为 \(1,2,3,\cdots,3n\),我们先将标号为 \(1,2,\cdots, n\) 的数分别放到一个集合里,这时候这些数构成了一个等差数列,其公差为 \(1\),那么接下来我们就要让剩下 \(2\) 组数的和构成一个公差为 \(-1\) 的等差数列。我们可以让一组数的大部分公差取到 \(1\) ,另一部分则取到 \(-2\),如果公差为 \(1\) 那么我们就让 \(i + 1\) 排在 \(i\) 的后面,如果公差为 \(-2\) 那么我们就让 \(i\) 排在 \(i + 2\) 的后面,不难看出 \(i\) 和 \(i + 2\) 的奇偶性相同,所以我们把 \(2n + 1, 2n + 2, \cdots, 3n\) 这 \(n\) 个数分类为奇数和偶数,相同的我们把 \(n + 1, n + 2, \cdots, 2n\) 也分为两类,每类的数量与下一组相同,因为 \(n\) 为奇数,所以可以看出这个数列的中心位置肯定是一个奇数 \(3n\) ,再考虑到公差为 \(-2\),那么前一位就是 \(2n + 2\) ,所以这一个位置他们的差为 \(n - 2\) ,我们要通过在另一组数中选取合适的数让他们的差为 \(-1\) ,可以看到 \((2n + 2) + 2n\) 与 \(3n + (n + 1)\) 的差也为1,所以我们就可以确定另外一组分成的两类的起始点和终点,这样我们就构造出了一种可行的排序方法。
Code
//B
#include<bits/stdc++.h>
typedef long long valueType;
void solve(valueType N, valueType K);
int main() {
std::ios::sync_with_stdio(false);
int T;
std::cin >> T;
for(int i = 1; i <= T; ++i) {
valueType N, K;
std::cin >> N >> K;
solve(N, K);
}
std::cout << std::flush;
return 0;
}
void solve(valueType N, valueType K) {
if(N == 1 && K == 1) {
std::cout << "Yes\n1\n";
return;
}
valueType const sum = N * (N + 1) / 2;
if(sum % K != 0 || N == K) {
std::cout << "No\n";
return;
}
valueType const count = N / K;
std::vector<std::queue<valueType>> ans(K);
if(count & 1) {
if((K & 1) == 0) {
std::cout << "No\n";
return;
}
valueType i = 1;
valueType const mid = (K + 1) / 2;
for(int j = 0; j < K; ++j)
ans[j].push(i++);
for(int j = mid; j < K; ++j)
ans[j].push(i++);
for(int j = 0; j < mid; ++j)
ans[j].push(i++);
i += K - 1;
for(int j = 0, p = i; j < mid; ++j, p -= 2)
ans[j].push(p);
for(int j = mid, p = i - 1; j < K; ++j, p -= 2)
ans[j].push(p);
++i;
for(int c = 0; c < count - 3; ++c) {
if(c & 1) {
for(int j = 0; j < K; ++j)
ans[j].push(i++);
} else {
for(int j = K - 1; j >= 0; --j)
ans[j].push(i++);
}
}
} else {
valueType i = 1;
for(int c = 0; c < count; ++c) {
if(c & 1) {
for(int j = 0; j < K; ++j)
ans[j].push(i++);
} else {
for(int j = K - 1; j >= 0; --j)
ans[j].push(i++);
}
}
}
std::cout << "Yes\n";
for(auto &iter : ans) {
while(!iter.empty()) {
std::cout << iter.front() << ' ';
iter.pop();
}
std::cout << '\n';
}
}
T3 混凝土粉末
这道题我们可以在 \(x\) 轴上建立线段树,把每个询问都对应到线段树的一个叶子节点上,我们发现一个询问可以得出答案当且仅当这个叶子节点上被累加的值超过询问的纵坐标。所以我们可以令叶子节点的权值为这一个点上待处理的询问中 \(y\) 值的最小值。
于是我们就可以类比出非叶子节点的权值:当前区间内的待处理的询问中 \(y\) 值的最小值。对于每次更改如果当前累加的值大于线段树的权值,我们就向下递归到被处理的询问的叶子节点去处理询问。每个询问最多引发一次向下递归,所以对处理所有操作的复杂度为 \(\displaystyle \mathcal{O}(QlogN)\),对询问进行排序的复杂度为 \(\displaystyle \mathcal{O}(QlogQ)\),建立线段树的复杂度为 \(\displaystyle \mathcal{O}(NlogN)\),总复杂度为 \(\displaystyle \mathcal{O}(QlogQ + QlogN + NlogN)\),在 \(Q, N\) 同阶的情况下,复杂度可看作 \(\displaystyle \mathcal{O}(NlogN)\)。
考虑到时间线对询问结果的影响,我们可以在进行更改操作的同时按照时间序列将之间的询问结果直接输出,这样就可以避免了非法时间的更改对询问结果的影响。
考场出现 MLE 是因为在存储每个节点对应的查询的时候使用了 std::queue
,在存储少量数据的情况下(例如只有一个),std::vector
的空间要明显优于 std::queue
,具体原因可见下文引用自 cppreference.com 的内容。
deque 的存储按需自动扩展及收缩。扩张 deque 比扩张 std::vector 更优,因为它不涉及到复制既存元素到新内存位置。另一方面,deque 典型地拥有较大的最小内存开销;只保有一个元素的 deque 必须分配它的整个内部数组(例如 64 位 libstdc++ 上是对象尺寸的 8 倍;64 位 libc++ 上是对象尺寸的 16 倍和 4096 字节中的较大者)。
Code
//C
#include<bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
constexpr valueType MAX = LLONG_MAX >> 1;
ValueVector ans;
struct Query {
int x;
valueType y;
int id;
void solve(valueType x) {
ans[id] = x;
}
Query(valueType _x_, valueType _y_, int i) : x(_x_), y(_y_), id(i) {};
friend bool operator<(const Query &a, const Query &b) {
if(a.y != b.y)
return a.y > b.y;
return a.id > b.id;
}
};
typedef std::vector<Query> QueryVector;
typedef QueryVector::iterator QueryIter;
typedef std::vector<QueryIter> QueryQueue;
struct Operation{
enum TYPE {
MODIFY = 1,
QUERY = 2
};
TYPE type;
int l, r, x;
valueType h;
};
typedef std::vector<Operation> OperateVector;
std::vector<QueryQueue> TreeQuery;
OperateVector operate;
QueryVector query;
class Tree{
private:
struct NODE{
valueType sum;
valueType data;
valueType lazy;
NODE() : sum(0), data(MAX), lazy(0) {};
};
int size;
std::vector<NODE> node;
void pushUp(int id) {
node[id].data = std::min(node[id << 1].data, node[id << 1|1].data);
}
void pushDown(int id) {
if(node[id].lazy != 0) {
node[id << 1].lazy += node[id].lazy;
node[id << 1].data -= node[id].lazy;
node[id << 1].sum += node[id].lazy;
node[id << 1|1].lazy += node[id].lazy;
node[id << 1|1].data -= node[id].lazy;
node[id << 1|1].sum += node[id].lazy;
node[id].lazy = 0;
}
}
public:
Tree(int N) : size(N), node((size + 5) << 2) {
build(1, 1, N);
}
void update(int l, int r, valueType key, int value) {
update(1, l, r, 1, size, key, value);
}
private:
void build(int id, int l, int r) {
if(l == r) {
node[id].lazy = 0;
node[id].data = TreeQuery[r].empty() ? MAX : TreeQuery[r].back()->y;
return;
}
valueType const mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1|1, mid + 1, r);
pushUp(id);
}
void update(int id, int queryL, int queryR, int nodeL, int nodeR, valueType key, int value) {
if(nodeL == nodeR) {
node[id].sum += key;
if(node[id].data <= key) {
TreeQuery[nodeR].back()->solve(value);
TreeQuery[nodeR].pop_back();
while(!TreeQuery[nodeR].empty() && TreeQuery[nodeR].back()->y <= node[id].sum) {
TreeQuery[nodeR].back()->solve(value);
TreeQuery[nodeR].pop_back();
}
node[id].data = TreeQuery[nodeR].empty() ? MAX : TreeQuery[nodeR].back()->y - node[id].sum;
} else {
node[id].data = node[id].data == MAX ? MAX : node[id].data - key;
}
return;
}
if(queryL <= nodeL && nodeR <= queryR) {
node[id].sum += key;
if(node[id].data <= key) {
pushDown(id);
valueType const mid = (nodeL + nodeR) >> 1;
update(id << 1, queryL, queryR, nodeL, mid, key, value);
update(id << 1|1, queryL, queryR, mid + 1, nodeR, key, value);
pushUp(id);
} else {
node[id].data = node[id].data == MAX ? MAX : node[id].data - key;
node[id].lazy = node[id].data == MAX ? 0 : node[id].lazy + key;
}
return;
}
pushDown(id);
valueType const mid = (nodeL + nodeR) >> 1;
if(queryL <= mid)
update(id << 1, queryL, queryR, nodeL, mid, key, value);
if(queryR > mid)
update(id << 1|1, queryL, queryR, mid + 1, nodeR, key, value);
pushUp(id);
}
};
int main() {
int N, Q;
std::cin >> N >> Q;
operate.resize(Q + 1);
query.reserve(Q + 1);
TreeQuery.resize(N + 1);
ans.resize(Q + 1, 0);
for(int i = 1; i <= Q; ++i) {
int type;
std::cin >> type;
if(type == 1) {
int l, r;
valueType h;
std::cin >> l >> r >> h;
operate[i].type = Operation::MODIFY;
operate[i].l = l;
operate[i].r = r;
operate[i].h = h;
operate[i].x = i;
} else if(type == 2) {
int x;
valueType y;
std::cin >> x >> y;
query.emplace_back(x, y, i);
operate[i].type = Operation::QUERY;
operate[i].x = i;
}
}
query.shrink_to_fit();
std::sort(query.begin(), query.end());
for(auto iter = query.begin(); iter != query.end(); ++iter)
TreeQuery[iter->x].push_back(iter);
Tree tree(N);
for(int i = 1; i <= Q; ++i) {
if(operate[i].type == Operation::MODIFY) {
tree.update(operate[i].l, operate[i].r, operate[i].h, operate[i].x);
} else {
std::cout << ans[operate[i].x] << '\n';
}
}
std::cout << std::flush;
return 0;
}
T4 排水系统
我们可以先算出不考虑管道损坏情况下每个节点的期望流量值,接下来我们考虑管道损坏对节点流量的影响。
我们能观察到,若 \(x\) 点的排水管道堵塞,从 \(x\) 点经过的污水吨数必然不变。
因此,我们可以借此考虑 \((x, y)\) 堵塞的影响:我们可以将管道堵塞视为 \(y\) 的经过污水吨数凭空减少一个定值,而 \(x\) 其他出点的污水吨数凭空增加一个定值。
我们将这一定值计算出来,作为节点流量的初始值,进行第二次计算,这样我们就可以统计出考虑管道损坏情况下的节点流量值。
Code
//D
#include<bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
constexpr valueType MOD = 998244353;
class Inverse{
private:
valueType size;
ValueVector data;
public:
Inverse(valueType N) : size(N), data(N, 0) {
data[1] = 1;
for(valueType i = 2; i < N; ++i)
data[i] = (long long)(MOD - MOD / i) * data[MOD % i] % MOD;
}
valueType operator()(valueType i) const {
i %= MOD;
if(i < size)
return data[i];
return (MOD - MOD / i) * operator()(MOD % i) % MOD;
}
};
typedef std::pair<int, valueType> EdgePair;
typedef std::list<EdgePair> EdgeList;
typedef std::vector<EdgeList> EdgeSet;
int main() {
valueType N, M, R, K;
std::cin >> N >> M >> R >> K;
EdgeSet edge(N + 3);
ValueVector degree(N + 3, 0), sumA(N + 3, 0), in(N + 3, 0);
valueType S = 0;
typedef std::function<void(int, int, valueType)> addEdgeFunction;
addEdgeFunction addEdge = [&edge, °ree, &in, &sumA, &S] (int u, int v, valueType weight) mutable {
edge[u].emplace_back(v, weight);
++degree[u];
++in[v];
S = (S + weight) % MOD;
sumA[u] = (sumA[u] + weight) % MOD;
};
for(int i = 0; i < K; ++i) {
int x, y;
valueType a;
std::cin >> x >> y >> a;
addEdge(x, y, a);
}
//前有绿豆蛙的归宿
//今有下水道的排出
Inverse Inv(N + 10);
valueType const InvS = Inv(S);
std::queue<int> que;
ValueVector dp(N + 3, 0);
for(int i = 1; i <= M; ++i) {
dp[i] = 1;
que.push(i);
}
ValueVector InCopy = in;
while(!que.empty()) {
int const x = que.front();
que.pop();
for(auto const &iter : edge[x]) {
int const to = iter.first;
dp[to] = (dp[to] + dp[x] * Inv(degree[x]) % MOD) % MOD;
if(--InCopy[to] == 0)
que.push(to);
}
}
ValueVector F(N + 3, 0);
for(int i = 1; i <= N; ++i) {
for(auto const &iter : edge[i]) {
int const to = iter.first;
int const weight = iter.second;
F[i] = ((__int128)F[i] + (__int128)dp[i] * Inv(degree[i] - 1) % MOD * weight % MOD * InvS % MOD) % MOD;
F[to] = (((__int128)F[to] - ((__int128)dp[i] * Inv(degree[i] - 1) % MOD * weight % MOD * InvS % MOD)) % MOD + MOD) % MOD;
}
}
InCopy = in;
for(int i = 1; i <= M; ++i) {
F[i] = (F[i] + 1) % MOD;
que.push(i);
}
while(!que.empty()) {
int const x = que.front();
que.pop();
for(auto const &iter : edge[x]) {
int const to = iter.first;
F[to] = (F[to] + F[x] * Inv(degree[x]) % MOD) % MOD;
if(--InCopy[to] == 0)
que.push(to);
}
}
for(int i = N - R + 1; i <= N; ++i)
std::cout << F[i] << ' ';
std::cout << std::endl << std::flush;
return 0;
}
NOIP模拟测试A3 赛后总结的更多相关文章
- 「题解」NOIP模拟测试题解乱写II(36)
毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组
2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色
2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)
2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...
- NOIP模拟测试17&18
NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...
- 2019.8.1 NOIP模拟测试11 反思总结
延迟了一天来补一个反思总结 急匆匆赶回来考试,我们这边大家的状态都稍微有一点差,不过最后的成绩总体来看好像还不错XD 其实这次拿分的大都是暴力[?],除了某些专注于某道题的人以及远程爆踩我们的某学车神 ...
- NOIP模拟测试1(2017081501)
好,今天是cgg第一次举行模拟测试,希望各位支持. 时间限制:2小时 题目链接: 题目一:水得都没名字了 题目二:车站 题目三:选数 不要觉得2小时太少,我的题目很良心,都很简单. 答案可以在模拟测试 ...
- 「题解」NOIP模拟测试题解乱写I(29-31)
NOIP模拟29(B) T1爬山 简单题,赛时找到了$O(1)$查询的规律于是切了. 从倍增LCA那里借鉴了一点东西:先将a.b抬到同一高度,然后再一起往上爬.所用的步数$×2$就是了. 抬升到同一高 ...
- 2019.8.14 NOIP模拟测试21 反思总结
模拟测试20的还没改完先咕着 各种细节问题=错失190pts T1大约三分钟搞出了式子,迅速码完,T2写了一半的时候怕最后被卡评测滚去交了,然后右端点没有初始化为n…但是这样还有80pts,而我后来还 ...
- 2019.8.9 NOIP模拟测试15 反思总结
日常爆炸,考得一次比一次差XD 可能还是被身体拖慢了学习的进度吧,虽然按理来说没有影响.大家听的我也听过,大家学的我也没有缺勤多少次. 那么果然还是能力问题吗……? 虽然不愿意承认,但显然就是这样.对 ...
随机推荐
- vue中使用vue.extend在dom挂载vue实例
const CounterComponent = Vue.extend(Counter); this.vm = new CounterComponent({}).$mount('#container' ...
- MySQL相关知识点思维导图整理
MySQL相关知识点思维导图整理 Xmind思维导图下载地址: 蓝奏云:https://shuihan.lanzoui.com/iXZw7frkn5a
- 垃圾回收之CMS、G1、ZGC对比
ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,它的设计目标包括: 停顿时间不超过10ms: 停顿时间不会随着堆的大小,或者活跃对象的大小而增加: ...
- [WEB]对于"Refused to execute script from 'http://xx.xx.xx/yy/zz.js' because its MIME type ('') is not executable, and strict MIME type checking is enabled."问题的解决办法
1 文由 阶段一:对WEB服务器HTTP通信的header设置了安全头(X-Content-Options:nosniff) 两个月前协助交付侧大哥处理.修复一个三级等保项目的第三方安全公司释出的安全 ...
- Springfox与SpringDoc——swagger如何选择(SpringDoc入门)
本文分享自天翼云开发者社区@<Springfox与SpringDoc--swagger如何选择(SpringDoc入门)>,作者: 才开始学技术的小白 0.引言 之前写过一篇关于swagg ...
- 【SpringCloud】(一)分布式理论
分布式架构理论 方法远程调用 各个模块运行于不同的tomcat,模块之间通过网络进行调用. 远程调用的技术演进 1 WebService 解决应用程序之间的跨平台访问问题,基于SOAP/WSDL协议, ...
- 动态规划DP入门问题----最大连续子序列,最长不下降子序列(可以不连续),最长公共子序列
一.最大连续子序列 1.题目叙述 对于一个数字序列A1A2A3...An,求出连续子序列的最大和,如对于序列-2,11,-4,13,-5,-2,其中的最大序列和是11+(-4)+13=20 2.动态规 ...
- 吃透SpringMVC面试八股文
说说你对 SpringMVC 的理解 SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块. 它通过一套注解,让一个简单的Jav ...
- python轻量级性能工具-Locust
Locust基于python的协程机制,打破了线程进程的限制,可以能够在一台测试机上跑高并发 性能测试基础 1.快慢:衡量系统的处理效率:响应时间 2.多少:衡量系统的处理能力:单位时间内能处理多少个 ...
- 【解决方法】域控制器升级报错-Verification of prerequisites for Domain Controller promotion failed.......
目录-快速跳转 问题描述 原因分析: 解决方案: 附言: 问题描述 在 VM 内 windows 2019 中域控制器安装完成后进行第一次设置进行Prerequisites Check(先决条件检查) ...