Submission #1313741

#TimeUsernameProblemLanguageResultExecution timeMemory
1313741orzorzorzBitaro’s Party (JOI18_bitaro)C++20
7 / 100
2113 ms351148 KiB
#include<bits/stdc++.h> using namespace std; // 根據題目限制 N=10^5, B 取 sqrt(N) 約 320 左右效果最好 const int mxN = 1e5 + 5; const int B = 320; vector<int> adj[mxN]; // best[i] 存儲到達節點 i 的最長路徑候選列表: {距離, 起點} vector<pair<int, int>> best[mxN]; // 大查詢用的 DP 陣列 int dp[mxN]; bool is_busy[mxN]; // 用陣列取代 set 檢查 busy,O(1) 查詢 int main() { // 必要的 IO 優化 ios::sync_with_stdio(0); cin.tie(0); int n, m, q; cin >> n >> m >> q; for(int i = 0; i < m; i++) { int u, v; cin >> u >> v; --u, --v; adj[u].push_back(v); } // --- 預處理 (Precalc) --- // 因為題目保證 S < E,直接 0~N 迴圈即為拓撲序 for(int i = 0; i < n; i++) { // 1. 加入自己作為起點 (距離 0) best[i].push_back({0, i}); // 2. 排序並去重 (Sort & Unique) // 目標:只保留距離最遠的前 B 個「不同起點」 sort(best[i].rbegin(), best[i].rend()); vector<pair<int, int>> temp; // 用一個簡單的迴圈去重,只要湊滿 B 個就停 // 這裡不需要 set,因為 B 很小,線性掃描 vector 檢查重複很快 // 或者使用 visited 陣列標記已經加入的 source // 為了極致效率,這裡用一個臨時的小 vector 檢查重複即可 // 由於 B 很小,N^2 檢查其實也只有 320*320,且通常遠小於此 // 更好的方式是用一個 static visited array,但要記得清空 // 這裡採用簡單掃描法 // 收集前 B 個不重複來源 // 為了避免每次都掃描 temp,我們可以用一個標記陣列 // 但要注意每次清空的成本。 // 簡單寫法: int cnt = 0; vector<pair<int,int>> unique_best; // 這裡可以用一個 static bitset 或 array 來判斷 source 是否出現過 // 但因為 source id 可能很大,我們用一個簡單的策略: // 因為已經按距離大到小排序,遇到的第一個 source X 一定是來自 X 的最長路徑 static int seen[mxN]; // 標記 source 是否已加入 static int timer = 0; // 用時間戳記來避免 memset timer++; for(auto &p : best[i]) { if(cnt >= B) break; if(seen[p.second] != timer) { seen[p.second] = timer; unique_best.push_back(p); cnt++; } } best[i] = unique_best; // 更新 best[i] 為剪枝後的結果 // 3. 傳播給鄰居 (Propagate) // 關鍵:這時候 best[i] 只有 B 個元素,所以複製很快 for(int v : adj[i]) { for(auto &p : best[i]) { best[v].push_back({p.first + 1, p.second}); } } } // --- 處理查詢 --- while(q--) { int t, y; cin >> t >> y; --t; vector<int> busy_list(y); for(int i = 0; i < y; i++) { cin >> busy_list[i]; --busy_list[i]; } if(y < B) { // 小查詢:直接查表 int ans = -1; for(auto &p : best[t]) { bool busy = false; for(int b : busy_list) { if(b == p.second) { busy = true; break; } } if(!busy) { ans = p.first; break; } } cout << ans << "\n"; } else { // 大查詢:暴力 DP // 標記 busy 節點 for(int b : busy_list) is_busy[b] = true; // 初始化 DP // 只需要跑到 t 即可,後面的點不影響 for(int i = 0; i <= t; i++) dp[i] = -1e9; for(int i = 0; i <= t; i++) { if(!is_busy[i]) { dp[i] = max(dp[i], 0); // 自己可以是起點 } // 如果此點不可達 (dp[i] < 0),就不更新鄰居 if(dp[i] >= 0) { for(int v : adj[i]) { if(v <= t) { // 優化:只更新 t 之前的點 dp[v] = max(dp[v], dp[i] + 1); } } } } cout << (dp[t] < 0 ? -1 : dp[t]) << "\n"; // 還原 busy 標記 (比 memset 快) for(int b : busy_list) is_busy[b] = false; } } return 0; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...