給出乙個長度為 \(n\) 的序列 \(a\),和乙個限制值 \(m\)。
每一輪需要取走一些數:
在滿足上述取數方案的情況下,一共會進行多少輪?
資料範圍:\(1 \leq n \leq 5 \times 10^4\),\(1 \leq m, a_i \leq 10^9\)。
莽!模擬每一輪取數的過程。
在前幾輪取數都沒有問題的時候,顯然貪心地在剩下的數中從最小的數乙個乙個選起,可以得到該輪取走的數的數目,不妨記這個量為 \(k\)。
然後可以一位一位地確定這 \(k\) 個取走的數的下標。
假設當前已經欽定了前 \(t\) 個取走的數的下標 \(p_1, p_2, \cdots, p_t\)。
因為要滿足字典序盡量大,這個 \(p_\) 在滿足題目要求的條件下一定是越靠後越好。
那麼此時選定的 \(p_\) 需要滿足:在區間 \([p_, n]\) 中前 \(k - t\) 小的數的和不超過 \(m - \sum\limits_^a_\)。
那麼二分答案即可。
使用的資料結構需要支援:單點修改 + 字尾前 \(k\) 小查詢。
可以使用樹狀陣列套權值線段樹,樹狀陣列維護字尾。
修改和查詢的時間複雜度都是 \(\mathcal(\log^2 n)\),二分答案還有乙個 \(\mathcal(\log n)\)。
於是總的時間複雜度為 \(\mathcal(n \log^3 n)\)。
#include #include #include #include using namespace std;
inline int read()
while (s >= '0' && s <= '9')
return x * f;
}const int n = 50010, mlognlogn = 20001000;
const int size = 1e9 + 10;
int n, m;
int a[n];
multisets;
int ct, root[n];
struct segmenttree t[mlognlogn];
int new()
void insert(int &p, int l, int r, int delta, int cv, int sv)
void add(int x, int delta, int cv, int sv)
vectorrt;
void prework(int x)
long long ask(int l, int r, int k) else
}vectorrecover;
void round()
for (int i = recover.size() - 1; i >= 0; i --)
int res = m;
int last = 0;
for (int t = 1; t <= k; t ++)
last = l;
res -= a[l];
s.erase(s.find(a[l]));
add(l, a[l], -1, -a[l]); }}
int main()
給出一棵 \(n\) 個點的樹,每個點都有乙個權值 \(a_i\)。
可以在樹上選定乙個起點 \(s\) 和乙個終點 \(t\)。
你需要最大化從 \(s\) 到 \(t\) 的簡單路徑上所有點按順序組成的權值序列的最長上公升子串行的長度。
資料範圍:\(1 \leq n \leq 10^5\),\(1 \leq a_i \leq 10^9\)。
比較一眼吧 ...
可以列舉 \(s\) 到 \(t\) 的簡單路徑上最高(深度最小)的那個點 \(p\)。
那麼這個簡單路徑可以被拆成 \(p\) 以及從 \(p\) 開始往下走的兩條路徑,一條路徑包含了 \(s\),一條路徑包含了 \(t\)。
分兩種情況:
可以考慮線段樹合併。
權值線段樹的每個節點上需要維護 \(st, ed\):分別表示以權值在 \([l, r]\) 內的點為起點的 lis 的最大長度(最長下降鏈),以權值在 \([l, r]\) 內的點為終點的 lis 的最大長度(最長上公升鏈)。
時間複雜度 \(\mathcal(n \log n)\)。
#include #include #include using namespace std;
inline int read()
while (s >= '0' && s <= '9')
return x * f;
}const int n = 100100, mlogn = 10001000;
const int size = 1e9 + 1;
int n;
int a[n];
int tot, head[n], ver[n * 2], next[n * 2];
void add(int u, int v)
int ans = 1;
int ct, root[n];
struct segmenttree
} t[mlogn];
int new()
void insert(int &p, int l, int r, int delta, int val, int type)
segmenttree ask(int p, int l, int r, int s, int e)
if (mid < e)
return self;
}int merge(int p, int q)
void dfs(int u, int fa)
insert(root[u], 0, size, a[u], st + 1, 1);
insert(root[u], 0, size, a[u], ed + 1, 2);
}int main()
dfs(1, 0);
printf("%d\n", ans);
return 0;
}
有 \(n\) 個人,以及 \(m\) 盤菜。
其中,第 \(i\) 個人對第 \(j\) 盤菜的評價為 \(a_\)。
特別地,如果 \(a_ = -1\),則表示第 \(i\) 個人不喜歡第 \(j\) 盤菜。
若某個人在餐桌上看到了自己不喜歡的菜,那麼他會被氣走。
在確定了上菜方案的情況下。
若第 \(i\) 個人在場,第 \(j\) 盤菜也在場,那麼你就會獲得 \(a_\) 的收益。
請確定乙個上菜方案,使得你獲得的收益最大化。
資料範圍:\(1 \leq n \leq 20\),\(1 \leq m \leq 10^6\)。
考慮列舉最後在場的人的集合 \(s\),設在場的人的集合為 \(s\) 的時候的收益為 \(f(s)\)。
因為不容易直接求出所有 \(f\) 值,所以考慮構造乙個輔助函式 \(g\),滿足:
\[f(s) = \sum\limits_ g(t)
\]對於第 \(i\) 個人對第 \(j\) 盤菜的評價 \(a_ \geq 0\):
\[g(t_j) \gets_+ a_ \\
g(t_j \setminus \) \gets_- a_
\]如果第 \(j\) 道菜最終在場,那麼 \(s\) 一定是 \(t_j\) 的子集,即 \(s \subseteq t_j\)。
上述操作的第一步是:計算所有不排斥第 \(j\) 盤菜的人的貢獻。
上述操作的第二步是:扣除所有不排斥第 \(j\) 盤菜,且不在場的人的貢獻。
這樣即可確保貢獻正確計算。
知道了 \(g\) 之後即可還原 \(f\)。
時間複雜度 \(\mathcal(2^nn + nm)\)。
#include #include #include using namespace std;
inline int read()
while (s >= '0' && s <= '9')
return x * f;
}const int n = 21, m = 1001000;
int n, m;
int a[n][m];
int t[m];
int f[1 << n];
int main()
for (int i = 0; i < n; i ++)
for (int s = 0; s < (1 << n); s ++)
if (s & (1 << i)) f[s ^ (1 << i)] += f[s];
int ans = 0;
for (int s = 0; s < (1 << n); s ++)
ans = max(ans, f[s]);
printf("%d\n", ans);
return 0;
}
(待填 ...) 比賽題解 NOIP2021 題解
可以先把十進位制表示下數字含有 7 的所有數都求出來,然後去列舉這些數的倍數,將其標記。如果當前列舉到的數被標記過了則就不需要再列舉倍數了 因為列舉的倍數肯定也被標記過了 時間複雜度 mathcal n log log n include include include using namespac...
NOIP2021 簡要覆盤 題解
luogu7961.設 f i,j,k,l 表示考慮前 i 位,放了 j 個數,字尾 k 個 1 狀壓 i 及前三位結果為 l 的權值和 無序 轉移平凡,最後分配順序即可。luogu7962.有經驗的選手很快發現等價於交換差分陣列,並不難發現差分陣列一定是單谷的,證明不會,但想一下方差反應的是 資料...
JOI Open 中一些沒有題解的題的簡要題解
雖然好像沒啥人做,但還是先放著,相信總是有人來看的。容易說明最終路徑一定只有以下兩種情況 然後直接線段樹優化建圖跑 bfs 即可,總複雜度 o n log n 經過一些觀察,對於一條邊,他把整個圓分成了兩段弧,那麼與這條邊相交的邊至多只有 min 兩段弧長度 條。此時猜測最優構造中這個上界可以達到。...