這道題模擬能有\(80pts\),但是考場上我不看題,花了將近三個小時敲大模擬\(40pts\)。ios
這道題實際上模擬的話能夠分爲公元前、1582.10.4、1582.10.5~1600.12.31,1600.12.31之後,爲何要分到$$1600.12.31$$爲結點呢?後面每四百年蹦一次方便。c++
固然,咱們也能夠經過觀察樣例求解:數組
考慮到\(P=3000000\)天時剛好是\(3501.8.15\),再日後蹦\(400\)年要\(T\)即\(146097\)天。暴力預處理這些天數。而後小於等於\(C=3146097\)的天數直接輸出,大於的年月用\(C+r\% T\)天數輸出,年份\((r/T)\)加上\((P+r\%T)\)表明的年份便可。函數
無語,居然忘記特判了。\(1\)移\(64\)位就誰也救不了我了。優化
考場上不看題,暴力分都沒拿到。spa
這道題模擬、暴力\(20pts\)。更優地,咱們甚至能夠將乘法累積到變量,加法乘該積數逆用於優化常數。code
#include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #define CLR(x, y) memset(x,y,sizeof(x)) #define FOR(i, x, y) for(register int i=x;i<=y;++i) #define ROF(i, x, y) for(register int i=x;i>=y;--i) #define pii pair<int,int> using namespace std; const int N = 200000 + 5, mod = 998244353; struct Query { int t, p, v, c; vector <int> g; } q[N]; int n, m, Q, a[N], f[N]; template <typename T> void read(T &x) { bool mark = false; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true; for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0'; if(mark) x = -x; return; } void work(int p) { if(q[p].t == 1) a[q[p].p] = (a[q[p].p] + q[p].v) % mod; else if(q[p].t == 2) FOR(i, 1, n) a[i] = 1ll * a[i] * q[p].v % mod; else FOR(i, 0, q[p].g.size() - 1) work(q[p].g[i]); return; } int main() { read(n); FOR(i, 1, n) read(a[i]); read(m); FOR(i, 1, m) { read(q[i].t); switch(q[i].t) { case 1: { read(q[i].p), read(q[i].v); break; } case 2: { read(q[i].v); break; } case 3: { read(q[i].c); FOR(j, 1, q[i].c) { int tmp; read(tmp); q[i].g.push_back(tmp); } break; } } } read(Q); FOR(i, 1, Q) read(f[i]); FOR(i, 1, Q) work(f[i]); FOR(i, 1, n) printf("%d ", a[i]); puts(""); return 0; }
假設操做序列中只有加法或乘法操做,那麼調用順序毫無影響。排序
對於加法操做,咱們只須要把每一個函數對序列的影響記錄下來,計算每一位上的數的變量。具體來講,咱們能夠根據調用關係創建一張DAG,按照拓撲序計算出每個加法操做被調用次數,對於每個加法操做函數,能夠記錄它更新的位置,統計時將該位置上的數加上總次數便可。get
乘法操做雖則一模一樣,但咱們對於該問題,有不同的解法。考慮計算每個函數所產生的乘積貢獻值,將全部調用的函數乘積貢獻值(即\(dp1[i]\))統計一下便可。string
咱們將問題轉化:對於每個數\(a_i\),先不考慮加的數對它的影響,它最終的答案就是總乘積乘該數。那麼,該問題變爲了對於每個加法操做對整個序列的影響統計,能夠考慮模擬的整個過程。
該過程當中,會發現,設第\(f_i\)個爲加法操做,那麼,對於後面的全部的\(f_j(j>i)\),它們的乘法貢獻必定會做用於第\(f_i\)個操做的值,最後統計便可。也就是說\(val*mul\)。
爲了可以統計後面的乘積,使其可以對前面有影響,咱們倒序進行。
時間複雜度下降爲\(O(Q*m)\)。
#include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #define CLR(x, y) memset(x,y,sizeof(x)) #define FOR(i, x, y) for(register int i=x;i<=y;++i) #define ROF(i, x, y) for(register int i=x;i>=y;--i) using namespace std; const int N = 100000 + 5, M = 100000 + 5, mod = 998244353; typedef long long LL; vector <int> G[M]; int n, m, f[N], p[M], type[M]; LL a[N], mul = 1, v[M], add[N]; void dfs(int u) { if(type[u] == 1) { add[p[u]] = (add[p[u]] + 1ll * v[u] * mul % mod) % mod; return; } if(type[u] == 2) { mul = 1ll * mul * v[u] % mod; return; } int v; for(int i = G[u].size() - 1; i >= 0; -- i) { v = G[u][i]; dfs(v); } return; } template <typename T> void read(T &x) { bool mark = false; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true; for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0'; if(mark) x = -x; return; } int main() { read(n); FOR(i, 1, n) read(a[i]); read(m); FOR(i, 1, m) G[i].clear(); FOR(i, 1, m) { read(type[i]); switch(type[i]) { case 1: read(p[i]), read(v[i]); break; case 2: read(v[i]); break; case 3: int tmp; read(tmp); G[i].resize(tmp); FOR(j, 0, tmp - 1) read(G[i][j]); break; default: break; } } int T; read(T); FOR(i, 1, T) read(f[i]); CLR(add, 0); int u; ROF(i, T, 1) { u = f[i]; if(type[u] == 1) add[p[u]] = (add[p[u]] + v[u] * mul) % mod; else if(type[u] == 2) mul = 1ll * mul * v[u] % mod; else dfs(u); } FOR(i, 1, n) printf("%lld ", (1ll * a[i] * mul % mod + add[i]) % mod); puts(""); return 0; }
這樣作並非最好的。
考慮:一個加法被乘\(k\)次,至關於該函數被調用\(k\)次。
而函數的「調用函數」被調用的次數須要累加到該函數內。
定義一個數組\(dp[u]\)表明結點被調用了幾回。
若是該節點是個乘法調用,不用理它,由於乘法咱們已經計算完了;若是是一個「調用函數」那麼,接下來的步驟,就是來解決內部的函數調用的統計;若是是一個加法調用,能夠累加到\(add[i]\)第\(i\)位影響。
接下來,咱們統計全部在「調用函數」中被調用的函數調用次數。
拓撲排序。
不過,在TOP過程當中,要隨時記錄乘積,更新調用次數;另外,對於一個「調用函數」的調用順序,能夠倒序求解。
#include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<queue> #define CLR(x, y) memset(x,y,sizeof(x)) #define FOR(i, x, y) for(register int i=x;i<=y;++i) #define ROF(i, x, y) for(register int i=x;i>=y;--i) using namespace std; const int N = 100000 + 5, M = 100000 + 5, mod = 998244353; typedef long long LL; template <typename T> void read(T &x) { bool mark = false; char ch = getchar(); for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true; for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0'; if(mark) x = -x; return; } vector <int> G[M]; queue <int> Q; int n, m, f[N], p[M], type[M], deg[M]; LL a[N], mul = 1, v[M], dp1[M], dp[M], add[N]; void dfs(int u) { if(dp1[u] != -1) return; if(type[u] == 1) dp1[u] = 1; else if(type[u] == 2) dp1[u] = v[u]; else { dp1[u] = 1; int x; for(int i = 0; i < G[u].size(); ++ i) { x = G[u][i]; dfs(x); dp1[u] = 1ll * dp1[u] * dp1[x] % mod; } } } int main() { read(n); FOR(i, 1, n) read(a[i]); read(m); CLR(deg, 0); FOR(i, 1, m) G[i].clear(); FOR(i, 1, m) { read(type[i]); switch(type[i]) { case 1: read(p[i]), read(v[i]); break; case 2: read(v[i]); break; case 3: int tmp; read(tmp); G[i].resize(tmp); FOR(j, 0, tmp - 1) { read(G[i][j]); ++ deg[G[i][j]]; } break; default: break; } } CLR(dp1, -1), CLR(dp, 0); FOR(i, 1, m) if(!deg[i]) dfs(i); int u, T; LL w; read(T); FOR(i, 1, T) read(f[i]); ROF(i, T, 1) { u = f[i]; if(type[u] == 1) dp[u] = (dp[u] + mul) % mod; else if(type[u] == 2) mul = 1ll * mul * dp1[u] % mod; else dp[u] = (dp[u] + mul) % mod, mul = 1ll * mul * dp1[u] % mod; } CLR(add, 0); while(Q.size()) Q.pop(); FOR(i, 1, m) if(!deg[i]) Q.push(i); while(Q.size()) { u = Q.front(); Q.pop(); if(type[u] == 1) add[p[u]] = (add[p[u]] + dp[u] * v[u]) % mod; w = dp[u]; reverse(G[u].begin(), G[u].end()); for(int i = 0; i < G[u].size(); ++ i) { int x = G[u][i]; -- deg[x]; dp[x] = (dp[x] + w) % mod; w = 1ll * w * dp1[x] % mod; if(!deg[x]) Q.push(x); } } FOR(i, 1, n) printf("%lld ", (1ll * a[i] * mul % mod + add[i]) % mod); puts(""); return 0; }