發表文章

目前顯示的是有「TIOJ」標籤的文章

[TIOJ] 1902. 「殿仁.王,不認識,誰啊?」,然後他就死了……

題目連結: https://tioj.infor.org/problems/1902 直接回滾莫隊後套個 trie 來查區間 xor 最大值,複雜度 $O((N^2/K + QK) \log C)$ 之類的(沒仔細算),$K$好好挑一下就會過。 p.s. 好像好多古代人的解都是 $O(N^2 + Q)$ 😥 #include <algorithm> #include <functional> #include <iostream> #include <iterator> #include <numeric> #include <utility> #include <vector> int main() { std::cin.tie(nullptr)->sync_with_stdio(false); auto maxi = [](auto &a, auto b) { a = std::max(a, b); }; int n, q; std::cin >> n >> q; std::vector<uint32_t> a(1); std::copy_n(std::istream_iterator<int>(std::cin), n, std::back_inserter(a)); std::partial_sum(a.begin(), a.end(), a.begin(), std::bit_xor()); struct Q { int l, r, id; Q(int l_, int r_, int id_) : l(l_), r(r_), id(id_) {} }; static constexpr int K = 128; std::vector<std::vector<Q>> qs((n + K - 1) / K); for (int ...

[TIOJ] 2050. 尋找關節點 EXTREME

題目連結: https://tioj.infor.org/problems/2050 上次來這裡寫東西好像是超級久以前ㄌXD 貼一下昨天吃飯聽到的做法,但是我不會證明,求知道的人貼個 paper 給我 (。・∀・)ノ゙,只知道這是蔡孟宗教授在我高二那年二階講的算法。 這題重點是如何把一張圖的邊減少到 $O(N)$ 量級,但還是保持著一些必要的連通性。如果我昨天吃飯的時候沒聽錯的話,我們可以做 $k$ 次 BFS 來找出生成森林,每次找出來後就把它從原圖中拔掉,並記錄到我們最後要取的邊集中,如果要求雙連通分量的話,那 $k=2$ ,而像這題是某種三連通,所以 $k=3$。 所以這題就把邊減少到 $O(N)$ 量集後就可以拔掉每個點,跑一次 tarjan 找出所有其他割點,不過有一些小細節要注意一下,例如拔掉一條鍊的最邊邊兩個點不會讓圖變得不連通之類ㄉ。 &num;include <bits&sol;stdc&plus;&plus;&period;h> using namespace std&semi; const int N &equals; 2000 &plus; 5&semi; const int M &equals; 2000000 &plus; 5&semi; class BCC&lowbar;AP &lcub; &Tab;p...

[TIOJ] 2027. 腳步鬆散

題目連結: https://tioj.infor.org/problems/2027 不知道多久沒來這裡了XD,來提供一下兩個這題的做法。 一個是gamegame跟我講的做法,我覺得我應該以前都沒想過,所以紀錄一下這個有趣的作法。 這裡我們將一棵二元樹的節點集合用$\mathbb{T}$表示,並且定義幾個函數 $\text{lch}:\mathbb{T}\rightarrow\mathbb{T}, \text{lch}(x)=\text{left child of }x$ $\text{rch}:\mathbb{T}\rightarrow\mathbb{T}, \text{rch}(x)=\text{right child of }x$ $\text{sz}:\mathbb{T}\rightarrow\mathbb{N}\cup\{0\}, \text{sz}(x)=\text{size of }x$ $\text{w}:\mathbb{T}\rightarrow\mathbb{N}\cup\{0\}, \text{w}(x)=\min(\text{sz}(\text{lch}(x)), \text{sz}(\text{rch}(x)))$ 那首先有個引理:對於任意二元樹$T$都有$\displaystyle\sum_{u \in T}\text{w}(u) = \mathcal{O}(\text{sz}(T) \log \text{sz}(T))$,證明的部分就留給讀者好了(X) 白話文講一點就是如果有一個$N$個節點的二元樹,而我們對於他每個節點作的演算法都只跟那個節點小的子樹的大小有關的話,複雜度就會是$\mathcal{O}(N \log N \cdot \text{單一操作的複雜度} )$。 回到這題,可以發現如果一個區間$[L, R]$的最大值是$a_i$若且唯若$L \in [j, i]$(其中$j$是使得$j a_i$發生的最大的$j$)跟$R \in [i, j]$(其中$j$是使得$i a_i$發生的最小的$j$),而若是我們把這種性質拿去建成一棵二元樹(也就是讓可能的編號$L$都在$i$的左子樹,可能的編號$R$都在$i$的右子樹)的話我們就會拿到 笛卡爾樹 。 有了笛卡爾樹後,我們還需要一個支援插入、刪除、統計有多...

[TIOJ] 1726. Dice Wars

題目連結: http://tioj.infor.org/problems/1726 為什麼$O((N+Q)\sqrt{N})$跑得比$O((N+Q)\sqrt{N}log(N))$還要慢啊,我居然是倒數的,明明上面的人複雜度都慘的(? 總之我的想法是分塊,然後對每塊都用$O(N^2)$的方式算完每塊內的答案,接著如果答案是跨多個塊的話,則我們只要掃過每一塊,並記錄當前遇到最後面的a跟b出現在哪,然後再看看現在看到這塊的a跟b最前面出現在哪就好,完成後更新a,b的最後位置,再去檢查下一塊。 易知當塊大小為k時,複雜度為$O(Nk+\frac{NQ}{k})$故取$k=\sqrt{N}$時最佳。 #include <iostream> #include <vector> #include <utility> #include <unordered_map> using namespace std; const int N = 60025 + 5; const int SQRT_N = 245; const int INF = 1<<30; vector<pair<int,int>> que; unordered_map<int,int> ans[N], fi[SQRT_N], se[SQRT_N]; int arr[N]; int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); int n, q; cin >> n >> q; for(int i=0;i<n;i++) cin >> arr[i]; n = (n+SQRT_N-1)/SQRT_N*SQRT_N; for(int i=0;i<q;i++){ int x, y; cin >> x >> y; if(x > y) swap(x,y)...

[TIOJ] 2039. AI-666 賺多少

題目連結: https://tioj.infor.org/problems/2039 注意!以下是努力壓常數後過的$O(N \log N)$作法(雖然好像大家也都是$O(N \log C)$的aliens優化解)。 這裡講一下我的作法好了,首先我們將題目視成不斷將k變大並詢問當前最大是多少。若是只有一個k的時候,我們顯然是要找一組左低右高的區間$[a_l, a_r]$,那變成兩個的時候,我們發現其實有幾種可能,一種是在$l$左邊繼續找一組左低右高的區間,或是在$r$右邊找一組左低右高的區間,或是在$(l, r)$中找一組左高右低的區間,把他們的差加進答案即可。 所以我們可以用線段樹維護我們想知道某個區間內左高右低或左低右高的最大值,這樣我們就是每次從heap拿出一個最大的區間加進答案後,把它左右中三個區間同時再塞到heap裡,繼續做到直到沒有東東可以拿,或達到k就好。 不過一般的線段樹實作會TLE,所以我寫了個zkw加上自己寫heap後就可以AC了XD #pragma GCC optimize("Ofast") #include <bits/stdc++.h> using namespace std; namespace { #define FORCE_INLINE __attribute__((always_inline)) constexpr int INF = 1 << 30; constexpr int maxn = 2'000'000; template <typename T1, typename T2> struct Pair { T1 first; T2 second; }; struct V { int val, pos_small, pos_big; }; struct S { Pair<int, int> lo, hi; V v[2]; FORCE_INLINE S(Pair<int, int> small, Pair<int...

[TIOJ] 1711. Apple Tree

題目連結: http://tioj.infor.org/problems/1711 說個笑話,oToToT會寫程式。 搞了好久都不會做,然後一直假解QAQ 去網路上查到momo學長的解居然也是錯的(詳見底下測資) 10 6 522 288 963 788 756 856 18 412 378 789 2 1 3 2 4 2 5 2 6 5 7 6 8 4 9 1 10 4 最後是去問了minson才知道一個怪怪的解QQ,我到現在還是不知道這為何是好的(他說複雜度是$O(N^2)$),可能跟CF 729F有類的概念吧。 註(2019/04/24):似乎是因為某種兩個東西只會在他們的LCA被合起來 總之就是用一個顯然的事實就是我們一個(子)樹最多只能走他底下size步,所以我們就只要維護size,並且每次上界都用當前大小做dp即可。 dp狀態也很簡單 $ dp[i][j][0] := \text{第i個節點走j步後且要走回來的最大值}, dp[i][j][1] := \text{第i個節點走j步後,停在他底下某個人的最大值} $轉移就直接暴力混和背包即可。 #include <cstdio> #include <cstring> #include <vector> #include <algorithm> const int N = 1000 + 5; int w[N], dp[N][N<<1][2]; int tmp[N<<1][2], sz[N]; std::vector<int> G[N]; void dfs(int,int,int); int main(){ int n, k; scanf("%d%d", &n, &k); k = std::min(k, n+n); for(int i=1;i<=n;i++) scanf("%d", w+i); for(int i=1;i<n;i++) { int u, v; ...

[TIOJ] 1117. Walsh code

題目連結: http://tioj.infor.org/problems/1117 學測念不完,可是又要北市賽了QQ只好把市賽題刷一刷,希望至少市賽不要出事 最近真的好笨,只會一些straightforward的題目,這題還是被雷的才出來的。 總之核心問題就是我們不能一次做太多事、要一步一步來,畢竟他每次要輸出的字串也沒多長。 那所以我們就每次要知道第n個矩陣的第i,j元素,而這可以直接根據定義直接看他在哪一塊,並且遞迴下去就會得知答案了。 #include <bits/stdc++.h> using namespace std; bool go(int,int,int); int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); for(int n, i, a, b;cin >> n >> i >> a >> b;){ for(int j=a;j<=b;j++) cout << (go(n, i-1, j-1)?"+1":"-1") << " \n"[j==b]; } return 0; } bool go(int n, int x, int y) { if(n == 0) return 0; int tl = (1<<(n-1)); if(x >= tl and y >= tl) return go(n-1, x-tl, y-tl)^1; if(x >= tl) x -= tl; if(y >= tl) y -= tl; return go(n-1, x, y); }

[TIOJ] 1914. 彩色紙條

題目連結: https://tioj.infor.org/problems/1914 搞了好久,才發現我一開始的DP式是錯的QQ 這題難點應該就是DP式吧,不過我也不知道我怎麼想到的,所以這邊就給式子就好:$dp[i][j] = \min_{i \leq k 艾佛森括號 ),然後$dp[i][j]$的意思是把$[i, j]$填滿的最小步數,然後轉移方法就是枚舉中點,代表先把$[i, k]$填好再填$[k+1, j]$,而減一的部份則是因為明顯若首尾相同,那我們在填左邊的時候就可以順便拉一條過去到右邊,就不需要右邊又在畫一遍了。 #include <bits/stdc++.h> using namespace std; const int N = 200 + 5; int arr[N], dp[N][N]; int main(){ int t; scanf("%d", &t); while(t--){ int n, m; scanf("%d%d", &n, &m); for(int i=1;i<=n;i++) scanf("%d", arr+i); for(int i=1;i<=n;i++) dp[i][i]=1; for(int i=n;i>=1;i--){ for(int j=i+1;j<=n;j++){ dp[i][j]=1<<25; for(int k=i;k+1<=j;k++) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]); if(arr[i]==arr[j]) dp[i][j]--; } } pr...

[TIOJ] 1941. 直升機抓寶 (Helicopter)

題目連結: https://tioj.infor.org/problems/1941 這題應該可以輕鬆想到$O(N^2)$的DP方式,同時也會發現那樣子太慢。 仔細觀察DP的表格後,發現他是一個單調的表格,也就是由左至右遞增,同時由下至上遞增,接著研究一下每個轉移對DP表格的貢獻,發現他必定會將他自己那個區間的DP值+1,而後面則為了要維護單調的性質,會再跟他取max。 那這類題目,其實我們可以維護每一段的DP值,每次加入一個新的轉移來源後,也代表加入了一個斷點,但是同時也有可能把自身後面的斷點吃光光。 所以複雜度會是$O(N log N)$,因為每個斷點只會出現跟消失一次,而單次新增跟刪除了複雜度都是$O(log N)$ #include <bits/stdc++.h> using namespace std; class BIT{ private: int n; vector<int> arr; inline int lowbit(int x){return x&(-x);} void modify(int p, int x){ for(;p;p-=lowbit(p)) arr[p] += x; } public: void init(int n_){ n = n_; arr.clear(); arr.resize(n); } void modify(int l, int r, int v){ modify(l, -v); modify(r, v); } int query(int x){ int ret = 0; for(;x<n;x+=lowbit(x)) ret += arr[x]; ...

[TIOJ] 1067. C.互質任務

題目連結: http://tioj.infor.org/problems/1067 直接DP餘數即可,因為餘數$gcd(a, b) = gcd(a%b, b)$ #include <bits/stdc++.h> using namespace std; const int N = 1000 + 5; const int C = 10000 + 5; inline int gcd(int a, int b){return b?gcd(b, a%b):a;} int dp[2][C]; int main(){ ios_base::sync_with_stdio(0); cin.tie(0); bool me=0, he=1; int n, m; while(cin >> n >> m, n or m) { memset(dp[me], -1, sizeof(dp[me])); dp[me][0] = 0; for(int i=0;i<n;i++){ int x; cin >> x; for(int j=0;j<m;j++) if(dp[me][j] >= 0) { dp[he][j] = max(dp[he][j], dp[me][j]); dp[he][(j*10+x)%m] = max(dp[he][(j*10+x)%m], dp[me][j]+1); } memset(dp[me], -1, sizeof(dp[me])); swap(me, he); } int ans = 0; for(int i=0;i<m;i++) if(gcd(i, m) == 1){ ans...

[TIOJ] 1852. 分眼皮

題目連結: http://tioj.infor.org/problems/1852 有趣的題目XD,看到24先猜砍半枚舉,但想了一段時間才知道該怎麼讓枚舉出來的兩個集合跟答案有關連。 首先發現,假設最後選出來的三個數字分別是$(a, b, c)$(不失一般性假設$a \geq b \geq c$),也就是說假設在枚舉出來的第一個集合中有一個三元組$(x, y, z)$(沒有大小關係),那只要在第二個集合中找到$(x^\prime, y^\prime, z^\prime)$使得$x+x^\prime \geq y+y^\prime \geq z+z^\prime$就好。 接著考慮$a-b, c-b$,發現只要$a-b\geq 0 \land c-b \leq 0$就可以代表$a \geq b \geq c$,也就是說對於第一個集合中的$(x, y, z)$,我們就是要找到使得$x+x^\prime-(y+y^\prime) \geq 0 \land z+z^\prime - (y+y^\prime) \leq 0$(也就是$y^\prime - x^\prime \leq x-y \land y^\prime - z^\prime \geq z-y$)成立的$(x^\prime, y^\prime, z^\prime)$中,$x^\prime - z^\prime$最小的那個,因為他就代表$(x, y, z)$可以配出的最小值($x+x^\prime - (z+z^\prime)$)。 這樣,就會發現問題轉換成給你一堆二維帶權資料點($(y^\prime - x^\prime, y^\prime - z^\prime)$, $x^\prime - z^\prime$),請求出第一維大於等於某個數且第二維小於等於某個數中,值最小的那個。這問題簡單的對一維排序後,加上一些前(後)綴極值資料結構,再利用雙指針就可以解決了 #include <bits/stdc++.h> using namespace std; typedef int64_t lld; const int N = 24; const lld INF = 1LL<<60; class BIT{ private:...

[TIOJ] 1696. Problem F 橘子園保衛戰

題目連結: http://tioj.infor.org/problems/1696 看來我對重心剖分的理解還不夠QQ,一直想成重心樹的父子關係會跟原本的樹一樣。 我對於每個重心樹上的點維護他重心子樹們中$ dis \leq i, \forall 0 \leq i \leq D_{max} $的人有幾個(其中dis代表在原樹上的距離),以及扣掉某個小孩的子樹後的這坨值。接著對於每個人就當做一個詢問,從葉子往上走訪重心樹,同時查詢不走剛剛來的那個點的子樹,距離好的點有幾個。 #include <bits/stdc++.h> using namespace std; #define ALL(x) begin(x), end(x) #define PB push_back #define SZ(x) ((int)(x).size()) const int N = 100000 + 5; struct node{ int cur, dis; node(int a=-1,int b=0):cur(a),dis(b){} }; vector<node> path[N]; vector<int> sum[N], fa_sum[N]; bool done[N]; int sz[N], M[N], fa[N], dis[N], que[N], cen[N]; vector<int> G[N]; int CenDe(int); int Query(int,int); int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; cin >> n; for(int i=1;i<=n;i++) cin >> que[i]; for(int i=0;i<n-1;i++){ int u, v; cin >> u >> v; G[u].PB(...

[TIOJ] 1134. 1.蓋房子問題

題目連結: http://tioj.infor.org/problems/1134 想了想覺得把1換成-1, 0換成1之後會有好事情,然後似乎可以做類似最大子矩陣的方式,但是我不知道該怎麼用。 被雷了之後才發現其實我只要把兩個問題合在一起就好了,一個是最大子矩陣,一個是某種找有多少大於零的序列的問題。首先我們可以先對一維用前綴和之後枚舉頂跟底,接著就變成一維的問題了,變成一維的問題後就是要問說對於所有大於零的序列中,最長是多少,作法也是考慮枚舉前綴和,每次只要查小於當前前綴和的所有鍵值中最小的值是多少,接著就把當前前綴和當做鍵值,現在的位置當作值插到某種資料結構就好了。 #include <bits/stdc++.h> using namespace std; #define ALL(x) begin(x), end(x) const int N = 200 + 5; const int INF = 1<<30; class LiSan{ private: vector<int> vv; public: inline void init(){vv.clear();} inline void insert(int x){vv.push_back(x);} inline void done(){ sort(ALL(vv)); vv.resize(distance(vv.begin(), unique(ALL(vv)))); } inline int size(){return vv.size();} inline int get(int x){ return distance(vv.begin(), lower_bound(ALL(vv), x)); } inline int inv_get(int x){return vv[x];} ...

[TIOJ] 1152. 1.銀河帝國旅行社

題目連結: http://tioj.infor.org/problems/1152 裸的樹直徑題,那樹直徑要怎麼做呢?有個greedy的做法:先隨便從一個點DFS下去,走到最遠的地方後,再從那個點DFS下去走到離他最遠的點,則這兩個點中間的路徑就會是直徑。 #include <bits/stdc++.h> using namespace std; #define PB push_back typedef pair<int,int> PII; #define FF first #define SS second const int N = 10000 + 5; vector<int> G[N]; void dfs(int,int,int,PII&); int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; cin>>n; for(int i=0;i<n;i++){ int x; while(cin>>x and x!=-1){ G[i].PB(x); G[x].PB(i); } } PII ans = {0, -1}; dfs(0, 0, 0, ans); ans.SS = -1; dfs(ans.FF, ans.FF, 0, ans); cout<<ans.SS<<'\n'; return 0; } void dfs(int w, int f, int sum, PII& ret){ if(sum > ret.SS) ret = {w, sum}; for(auto i: G[w]){ if(i==f) continue;...

[TIOJ] 1221. 炒菜問題 / [POI] XII. Toy Cars

題目連結: http://tioj.infor.org/problems/1221 或 POI XII Toy Cars 我不知道怎麼證明QAQ... 總之做法就是要移掉東西的時候,就挑接下來最晚出現的移掉。而要維護誰最晚出現我這裡是開個陣列先預處理好對於每個$i$,紀錄下一個跟他一樣的數字是在哪裡。而接著掃過去的時候,每走到一個地方,就把這個地方的更新掉,這要樣pop東西的時候就可以拿到最新的資訊了。 #include <bits/stdc++.h> using namespace std; #define PB push_back #define FF first #define SS second const int N = 500000 + 5; const int M = 100000 + 5; map<int,int> mp; int nxt[N], tmp[M], arr[N]; bool ins[M]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n, k, p; cin>>n>>k>>p; for(int i=0;i<p;i++) nxt[i] = p+i; for(int i=0;i<=n;i++) tmp[i]=-1; for(int i=0;i<p;i++) cin>>arr[i]; for(int i=0;i<p;i++){ if(tmp[arr[i]]!=-1) nxt[tmp[arr[i]]] = i; tmp[arr[i]]=i; } int size = 0, ans = 0; for(int i=0;i<p;i++){ if(!ins[arr[i]]){ if(size == k){ ...

[TIOJ] 1721. 山上的風景

題目連結: http://tioj.infor.org/problems/1721 往右往左各分別維護一個遞減的stack,而若是塞入的東西比較大,則就把比他小的全部pop掉,同時也保證他們都只能看到這裡。 #include <bits/stdc++.h> using namespace std; #define PB push_back typedef pair<int,int> PII; #define FF first #define SS second const int N = 100000 + 5; int arr[N], ans[N]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; while(cin>>n){ for(int i=0;i<n;i++) cin>>arr[i]; stack<PII> ss; for(int i=0;i<n;i++){ while(!ss.empty() and ss.top().FF <= arr[i]){ ans[ss.top().SS] = i-ss.top().SS+1; ss.pop(); } ss.push({arr[i], i}); } while(!ss.empty()){ ans[ss.top().SS] = n-ss.top().SS; ss.pop(); } for(int i=n-1;i>=0;i--){ while(!ss.empty() and ss.top().FF ...

[TIOJ] 1610. Problem D 搭橋 (BRIDGE)

題目連結: http://tioj.infor.org/problems/1610 乍看之下還以為是什麼奇怪的樹題,結果其實是個簡單的greedy題。 應該不難看出我們其實可以把船亂排、木板亂排,而答案會是$\sum_{i=0}^{n-1} L_i \sum_{j=0}^{i} W_j$,稍微觀察後會發現其實我們由小拿到到,然後木板由長的接到短的會是好的,因為你若拿了比較多再走長的一定會消耗較多體力。 #include <bits/stdc++.h> using namespace std; typedef long long lld; const int N = 20000 + 5; lld arr[N], woo[N]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; cin>>n; for(int i=0;i<n;i++) cin>>arr[i]; for(int i=1;i<n;i++) cin>>woo[i]; sort(arr, arr+n); sort(woo+1, woo+n, [](lld a, lld b){return a>b;}); lld cur = arr[0], ans = 0; for(int i=1;i<n;i++){ ans += cur*woo[i]; cur += arr[i]; } cout<<ans<<'\n'; return 0; }

[TIOJ] 1589. 蚯蚓之入侵雅勒問題 Athena

題目連結: http://tioj.infor.org/problems/1589 很明顯的可以知道到一個點的方法數,就是到所有連到他的人的方法數加起來,所以我們可以用類似DP的方法記錄從起點到每個點的方法數,而DP順序就按照拓樸排序就好了XD #include <bits/stdc++.h> using namespace std; #define PB push_back typedef long long lld; const int N = 250 + 5; vector<int> G[N]; lld way[N]; int in[N]; int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int vv, ee, m; cin>>vv>>ee>>m; for(int i=0;i<ee;i++){ int u, v; cin>>u>>v; G[u].PB(v); in[v]++; } int st, ed; cin>>st>>ed; way[st]=1; queue<int> qq; for(int i=0;i<vv;i++) if(in[i]==0) qq.push(i); while(!qq.empty()){ int u = qq.front(); qq.pop(); for(auto v: G[u]){ way[v] = (way[v]+way[u])%m; in[v]--; if(in[v]==0) qq.push(v); } } cout<...

[TIOJ] 1995. 桑京邀請賽

題目連結: http://tioj.infor.org/problems/1995 噁爛的壓常數題,吳勝福居然原本官方解答是自己手寫3bytes整數,有夠可怕,好加在鄭天鈞夠聰明,讓我們免於這種可怕的地獄。 他的做法是建立一個Sparse Table,然而我們會發現我們正常sparse table會用掉$O(n log n)$記憶體的原因是因為我們要可以應付在線詢問,然而像這題離線的其實可以用$O(n)$的記憶體就好了,而少掉那$logn$也可簡單,就是你每次用完一條就丟掉,因為建立某一條的時候就可以把符合那條區間的詢問全部算好。此外我這裡因為沒把詢問排序好是因為若排序好就要多一條的記憶體來記錄原本誰是誰,所以只好讓詢問退化成$logN$,每建好一條就把所有詢問檢查過一遍。 #include <cstdio> #include <algorithm> using namespace std; const int N = 2500000 + 1; const int M = 1000000 + 1; int L[M], R[M], arr[N]; int main(){ int n, m; scanf("%d%d",&n,&m); for(int i=0;i<m;i++){ scanf("%d%d",&L[i],&R[i]); L[i]--; R[i]--; } for(int i=0;i<n;i++) scanf("%d",&arr[i]); for(int j=0;(1<<j)<=n;j++){ for(int q=0;q<m;q++){ if(R[q]==-1) continue; int logN = 31-__builtin_clz(R[q]-L[q]+1); if(j!=logN) continu...

[TIOJ] 1021. G.Counting Page Numbers

題目連結: http://tioj.infor.org/problems/1021 這裡每個狀態存的是在這個狀態下有幾個k出現,而若現在再加一個k就是再加入前面數字的數量,所以我還先算了一下每個情況數字的數量。(我不太會數位DP,講的可能不是很好OAO #include <bits/stdc++.h> using namespace std; typedef long long lld; const int LogN = 10 + 5; int dig[LogN], k, step; lld cnt[LogN][2][2], dp[LogN][2][2]; int cnted[LogN][2][2], dped[LogN][2][2]; lld calc(int,bool,bool); lld go(int,bool,bool); int main(){ ios_base::sync_with_stdio(0);cin.tie(0); int n; while(cin>>n>>k){ step++; int logN; for(logN = 0;n>0;logN++){ dig[logN] = n%10; n/=10; } calc(logN, 0, 0); cout<<go(logN, 0, 0)<<'\n'; } return 0; } lld calc(int pos, bool any, bool start){ if(pos < 0){ cnt[pos+1][any][start]= start; return start; } if(cnted[pos+1][any][st...