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

【拓扑序 时间倒流法】P7077 [CSP-S2020] 函数调用|省选-

本文涉及的知识点

C++图论 拓扑序
时间倒流法

P7077 [CSP-S2020] 函数调用

题目描述

函数是各种编程语言中一项重要的概念,借助函数,我们总可以将复杂的任务分解成一个个相对简单的子任务,直到细化为十分简单的基础操作,从而使代码的组织更加严密、更加有条理。然而,过多的函数调用也会导致额外的开销,影响程序的运行效率。

某数据库应用程序提供了若干函数用以维护数据。已知这些函数的功能可分为三类:

  1. 将数据中的指定元素加上一个值;
  2. 将数据中的每一个元素乘以一个相同值;
  3. 依次执行若干次函数调用,保证不会出现递归(即不会直接或间接地调用本身)。

在使用该数据库应用时,用户可一次性输入要调用的函数序列(一个函数可能被调用多次),在依次执行完序列中的函数后,系统中的数据被加以更新。某一天,小 A 在应用该数据库程序处理数据时遇到了困难:由于频繁而低效的函数调用,系统在执行操作时进入了无响应的状态,他只好强制结束了数据库程序。为了计算出正确数据,小 A 查阅了软件的文档,了解到每个函数的具体功能信息,现在他想请你根据这些信息帮他计算出更新后的数据应该是多少。

输入格式

第一行一个正整数 nnn,表示数据的个数。
第二行 nnn 个整数,第 iii 个整数表示下标为 iii 的数据的初始值为 aia_iai
第三行一个正整数 mmm,表示数据库应用程序提供的函数个数。函数从 1∼m1 \sim m1m 编号。
接下来 mmm 行中,第 jjj1≤j≤m1 \le j \le m1jm)行的第一个整数为 TjT_jTj,表示 jjj 号函数的类型:

  1. Tj=1T_j = 1Tj=1,接下来两个整数 Pj,VjP_j, V_jPj,Vj 分别表示要执行加法的元素的下标及其增加的值;
  2. Tj=2T_j = 2Tj=2,接下来一个整数 VjV_jVj 表示所有元素所乘的值;
  3. Tj=3T_j = 3Tj=3,接下来一个正整数 CjC_jCj 表示 jjj 号函数要调用的函数个数,
    随后 CjC_jCj 个整数 g1(j),g2(j),…,gCj(j)g^{(j)}_1, g^{(j)}_2, \ldots , g^{(j)}_{C_j}g1(j),g2(j),,gCj(j) 依次表示其所调用的函数的编号。

m+4m + 4m+4 行一个正整数 QQQ,表示输入的函数操作序列长度。
m+5m + 5m+5QQQ 个整数 fif_ifi,第 iii 个整数表示第 iii 个执行的函数的编号。

输出格式

一行 nnn 个用空格隔开的整数,按照下标 1∼n1 \sim n1n 的顺序,分别输出在执行完输入的函数序列后,数据库中每一个元素的值。答案对 998244353\boldsymbol{998244353}998244353 取模。

输入输出样例 #1

输入 #1

3
1 2 3
3
1 1 1
2 2
3 2 1 2
2
2 3

输出 #1

6 8 12

输入输出样例 #2

输入 #2

10
1 2 3 4 5 6 7 8 9 10
8
3 2 2 3
3 2 4 5
3 2 5 8
2 2
3 2 6 7
1 2 5
1 7 6
2 3
3
1 2 3

输出 #2

36 282 108 144 180 216 504 288 324 360

输入输出样例 #3

输入 #3

见附件中的 call/call3.in

输出 #3

见附件中的 call/call3.ans

说明/提示

【样例 #1 解释】

111 号函数功能为将 a1a_1a1 的值加一。222 号函数功能为所有元素乘 222333 号函数将先调用 111 号函数,再调用 222 号函数。

最终的函数序列先执行 222 号函数,所有元素的值变为 2,4,62, 4, 62,4,6

再执行 333 号函数时,先调用 111 号函数,所有元素的值变为 3,4,63, 4, 63,4,6。再调用 222 号函数,所有元素的值变为 6,8,126, 8, 126,8,12

【数据范围】

测试点编号n,m,Q≤n, m, Q \len,m,Q∑Cj\sum C_jCj其他特殊限制
1∼21 \sim 212100010001000=m−1= m - 1=m1函数调用关系构成一棵树
3∼43 \sim 434100010001000≤100\le 100100
5∼65 \sim 656200002000020000≤40000\le 4000040000不含第 222 类函数或不含第 111 类函数
777200002000020000=0= 0=0
8∼98 \sim 989200002000020000=m−1= m - 1=m1函数调用关系构成一棵树
10∼1110 \sim 111011200002000020000≤2×105\le 2 \times 10^52×105
12∼1312 \sim 13121310510^5105≤2×105\le 2 \times 10^52×105不含第 222 类函数或不含第 111 类函数
14141410510^5105=0= 0=0
15∼1615 \sim 16151610510^5105=m−1= m - 1=m1函数调用关系构成一棵树
17∼1817 \sim 18171810510^5105≤5×105\le 5 \times 10^55×105
19∼2019 \sim 20192010510^5105≤106\le 10^6106

对于所有数据:0≤ai≤1040 \le a_i \le 10^40ai104Tj∈{1,2,3}T_j \in \{1,2,3\}Tj{1,2,3}1≤Pj≤n1 \le P_j \le n1Pjn0≤Vj≤1040 \le V_j \le 10^40Vj1041≤gk(j)≤m1 \le g^{(j)}_k \le m1gk(j)m1≤fi≤m1 \le f_i \le m1fim

拓扑序 时间倒流法

简化版:只有操作一操作二

依次执行n个操作ope,只有加法和乘法。如果i个操作是加法:b[i] 是要增加的数的下标,c[i]是要增加的值,d[i]是1。如果是乘法,b[i]=c[i]=0,d[i]等于要乘的数。suff[i] = Πk:0N−1d[k]\Pi_{k:0}^{N-1} d[k]Πk:0N1d[k]
性质一:以任意顺序执行以下操作各一次:a[b[i]]+=c[i]∗d[i]a[b[i]] += c[i]*d[i]a[b[i]]+=c[i]d[i]
性质二:两个相邻的乘法操作可以合并。即d[i] *= d[i+1],删除ope[i+1]。
注意一:a[i]的初值,转化成加法。
注意二:逆序处理,方便计算suff。

操作三

f(i,j)⟺\iff 依次执行ope[i...j]ope[i...j]ope[i...j]
性质二:依次执行以下四个操作: f(i,j) 乘以 x1 f(i,j) 乘以x2 ⟺\iff 依次执行以下两个操作 f(i,j) 乘以x2+x2×x1x2 + x2 \times x1x2+x2×x1

拓扑序

加法和乘法没有孩子。
操作三i调用的函数是它的孩子,由于逆序处理,故neiBo[i] = {gc⋯g1g_c \cdots g_1gcg1}
增加节点0,neiBo[0] = {f_{Q} \cdots f_1$}。

实现

vMul[u],u函数所有叶子节点d[i]之积,按拓扑序处理,Πv是u子节点vMul[v]\Pi_{v是u子节点}vMul[v]Πvu子节点vMul[v]
need[0]=1
按拓扑序逆序处理各节点u:
iMul = need[u]
如果是叶子节点: a[b[u]] += c[u] $\times $ iMul
如果非叶子节点:
for(const auto& v : neiBo[u]){
need[v] += iMul;
iMul *= vMul[v]
}

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T1, class T2, class T3, class T4,class T5 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t);return in;
}template<class T1, class T2, class T3, class T4, class T5,class T6 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5,T6>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >>get<5>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char  puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行		return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错			}m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};class CNeiBo
{
public:static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0){vector<vector<int>>  vNeiBo(n);for (const auto& [i1, i2] : edges){vNeiBo[i1 - iBase].emplace_back(i2 - iBase);if (!bDirect){vNeiBo[i2 - iBase].emplace_back(i1 - iBase);}}return vNeiBo;}static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0){vector<vector<int>>  vNeiBo(n);for (const auto& v : edges){vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);if (!bDirect){vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);}}return vNeiBo;}static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0){vector<vector<std::pair<int, int>>> vNeiBo(n);for (const auto& v : edges){vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);if (!bDirect){vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);}}return vNeiBo;}static vector<vector<std::pair<int, int>>> Three(int n, const vector<tuple<int, int, int>>& edges, bool bDirect, int iBase = 0){vector<vector<std::pair<int, int>>> vNeiBo(n);for (const auto& [u, v, w] : edges){vNeiBo[u - iBase].emplace_back(v - iBase, w);if (!bDirect){vNeiBo[v - iBase].emplace_back(u - iBase, w);}}return vNeiBo;}static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat){vector<vector<int>> neiBo(neiBoMat.size());for (int i = 0; i < neiBoMat.size(); i++){for (int j = i + 1; j < neiBoMat.size(); j++){if (neiBoMat[i][j]){neiBo[i].emplace_back(j);neiBo[j].emplace_back(i);}}}return neiBo;}
};class CDGTopSort
{
public:template <class T = vector<int> >CDGTopSort(const vector<T>& vNeiBo) :m_vDeg(vNeiBo.size()), m_neiBo(vNeiBo) {const int N = vNeiBo.size();m_backNeiBo.resize(N);for (int cur = 0; cur < N; cur++){m_vDeg[cur] = vNeiBo[cur].size();for (const auto& next : vNeiBo[cur]){m_backNeiBo[next].emplace_back(cur);}}}void Init() {auto Add = [&](int i) {if (0 != m_vDeg[i]) { return; }m_que.emplace(i);};for (int i = 0; i < m_vDeg.size(); i++){Add(i);}while (m_que.size()){const int cur = m_que.front(); m_que.pop();if (!OnDo(cur)) { continue; }for (const auto& next : m_backNeiBo[cur]){m_vDeg[next]--;Add(next);}};}queue<int> m_que;vector<int> m_vDeg;vector<int> m_vSort;
protected:const vector<vector<int>>& m_neiBo;vector<vector<int>> m_backNeiBo;virtual bool OnDo(int cur) {m_vSort.emplace_back(cur);return true;};
};template<long long MOD = 1000000007, class T1 = int, class T2 = long long>
class C1097Int
{
public:C1097Int(T1 iData = 0) :m_iData(iData% MOD){}C1097Int(T2 llData) :m_iData(llData% MOD) {}C1097Int  operator+(const C1097Int& o)const{return C1097Int(((T2)m_iData + o.m_iData) % MOD);}C1097Int& operator+=(const C1097Int& o){m_iData = ((T2)m_iData + o.m_iData) % MOD;return *this;}C1097Int& operator-=(const C1097Int& o){m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;return *this;}C1097Int  operator-(const C1097Int& o)const{return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);}C1097Int  operator*(const C1097Int& o)const{return((T2)m_iData * o.m_iData) % MOD;}C1097Int& operator*=(const C1097Int& o){m_iData = ((T2)m_iData * o.m_iData) % MOD;return *this;}C1097Int  operator/(const C1097Int& o)const{return *this * o.PowNegative1();}C1097Int& operator/=(const C1097Int& o){*this *= o.PowNegative1();return *this;}bool operator==(const C1097Int& o)const{return m_iData == o.m_iData;}bool operator<(const C1097Int& o)const{return m_iData < o.m_iData;}C1097Int pow(T2 n)const{C1097Int iRet = (T1)1, iCur = *this;while (n){if (n & 1){iRet *= iCur;}iCur *= iCur;n >>= 1;}return iRet;}C1097Int PowNegative1()const{return pow(MOD - 2);}T1 ToInt()const{return ((T2)m_iData + MOD) % MOD;}
private:T1 m_iData = 0;;
};class Solution {
public:vector<int> Ans(vector<int>& as, vector<vector<int>>& fun, vector<int>& que) {typedef C1097Int< 998244353> BI;vector<int> tmp(que.rbegin(), que.rend());que.swap(tmp);for (int i = 0; i < as.size(); i++) {fun.emplace_back(vector<int>{1, i + 1, as[i]});que.emplace_back(fun.size());}const int N = as.size(), M = fun.size(), NN = M + 1;vector<vector<int>> neiBo(NN);vector<int> b(NN);vector<BI> c(NN), vMul(NN, 1);neiBo[0] = que;for (int i = 0; i < M; i++) {if (1 == fun[i].front()) {b[i + 1] = fun[i][1] - 1;c[i + 1] = fun[i][2];}else if (2 == fun[i].front()) {vMul[i + 1] = fun[i][1];}else if (3 == fun[i].front()) {for (int j = fun[i].size() - 1; j >= 2; j--) {neiBo[1 + i].emplace_back(fun[i][j]);}}}CDGTopSort topSort(neiBo);topSort.Init();for (const auto& u : topSort.m_vSort) {for (const auto& v : neiBo[u]) {vMul[u] *= vMul[v];}}vector<BI> need(NN), biAns(N);need[0] = 1;for (const auto& u : vector<int>(topSort.m_vSort.rbegin(), topSort.m_vSort.rend())) {BI iMul = need[u];if (neiBo[u].empty()) {biAns[b[u]] += c[u] * iMul;continue;}for (const auto& v : neiBo[u]) {need[v] += iMul;iMul *= vMul[v];}}vector<int> ans(N);for (int i = 0; i < N; i++) {ans[i] = biAns[i].ToInt();}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUGios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);	int M ,kind,cnt;auto a = Read<int>();cin >> M   ;vector<vector<int>> fun(M);for (int i = 0; i < M; i++) {cin >> kind >> cnt;if (1 == kind) {		fun[i] = { 1,cnt,0 };cin >> fun[i].back();}else if (2 == kind) {fun[i] = { 2,cnt };}else {fun[i].resize(cnt + 2);fun[i].front() = 3;fun[i][1] = cnt;for (int j = 0; j < cnt; j++) {cin >> fun[i][2 + j];}}}auto que = Read<int>();
#ifdef _DEBUG//printf("N=%d",N);Out(a, ",a=");Out(fun, ",fun=");Out(que, ",que=");
#endif // DEBUGauto res = Solution().Ans(a,fun,que);for (const auto& i : res){cout << i << " ";}return 0;
}

单元测试

vector<int> a, que;vector<vector<int>> fun;TEST_METHOD(TestMethod01){a = { 1 }, fun = { {2,2},{1,1,3 } }, que = { 1,2 };auto res = Solution().Ans(a, fun, que);AssertV({ 5 }, res);}TEST_METHOD(TestMethod02){a = { 1 }, fun = { {2,2},{1,1,3 } }, que = { 2,1 };auto res = Solution().Ans(a, fun, que);AssertV({ 8 }, res);}TEST_METHOD(TestMethod11){a = { 1,2,3 }, fun = { {1,1,1},{2,2},{3,2,1,2} }, que = { 2,3 };auto res = Solution().Ans(a, fun, que);AssertV({6,8,12 }, res);}TEST_METHOD(TestMethod12){a = { 1,2,3,4,5,6,7,8,9,10 }, fun = { {3,2,2,3},{3,2,4,5},{3,2,5,8},{2,2},{3,2,6,7},{1,2,5},{1,7,6},{2,3} }, que = { 1,2,3 };auto res = Solution().Ans(a, fun, que);AssertV({ 36,282, 108, 144, 180 ,216 ,504 ,288, 324, 360 }, res);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

http://www.dtcms.com/a/315088.html

相关文章:

  • 嵌入式开发入门——电子元器件~电容
  • RLCraft开服踩坑记录
  • 防火墙web页面练习
  • 使用AWS for PHP SDK实现Minio文件上传
  • Centos7离线安装Mysql8.0版本
  • 政务云数智化转型:灵雀云打造核心技术支撑能力
  • HarmonyOS 多屏适配最佳实践:基于 ArkUI 的响应式 UI 方案
  • 在CentOS 7上安装配置MySQL 8.0完整指南
  • [Oracle] TO_NUMBER()函数
  • C 语言结构体与 Java 类的异同点深度解析
  • Hexo - 免费搭建个人博客07 - 添加右上角的“目录”
  • 《Python 实用项目与工具制作指南》· 2.4 pip
  • 流量见顶时代,知识付费 IP 的破局逻辑
  • 我的世界进阶模组开发教程——附魔(2)
  • 使用 IntelliJ IDEA + Spring JdbcTemplate 操作 MySQL 指南
  • 【无标题】文件IO与标准IO的区别
  • LeetCode 分类刷题:16. 最接近的三数之和
  • Vue 影院组件
  • BLIP 和 BLIP2 的对比
  • 如何实现人机协同与人工智能的深度协同发展?
  • 【龙芯99派新世界】2.buildroot使用,连接wifi
  • 英伟达Llama - Nemotron 253B:大模型训练范式的革新与展望
  • C++多线程同步:深入理解互斥量与事件机制
  • 情感AI在医疗领域的核心应用潜力与创新方向
  • 02324-离散数学-速记宝典
  • WSL安装Ubuntu与Docker环境,比VMware香
  • Sparse4D系列算法:迈向长时序稀疏化3D目标检测的新实践
  • Flutter开发 了解Scaffold
  • FinalShell 跳板机proxyjump使用
  • 105页PPT | 麦肯锡五年战略规划方法论精要