比賽題解 NOIP2021 題解

2022-09-14 17:42:21 字數 3721 閱讀 3710

可以先把十進位制表示下數字含有 \(7\) 的所有數都求出來,然後去列舉這些數的倍數,將其標記。

如果當前列舉到的數被標記過了則就不需要再列舉倍數了(因為列舉的倍數肯定也被標記過了)。

時間複雜度 \(\mathcal(n \log \log n)\)。

#include #include #include using namespace std;

inline int read()

while (s >= '0' && s <= '9')

return x * f;

}const int size = 1e7 + 500;

bool ori[size + 10];

bool f[size + 10];

int go[size + 10];

void prework(int n)

for (int i = n; i >= 1; i --)

if (f[i])

go[i] = go[i + 1];

else

go[i] = i;

}void work()

int main()

// i hope changle_cyx can pray for me.

注意到直接在序列後加入乙個數會比較難維護,可以考慮從小到大向序列中加數。

設 \(f(i, j, h, s)\) 表示:已經選了 \(j\) 個數,選出的數的值域為 \([0, i]\),二進位制表示中的 \([0, i]\) 位中有 \(h\) 個 \(1\),\([i + 1, *]\) 中的二進位制表示為 \(s\) 時的方案數。

可以考慮列舉選了多少個 \(i\)。假設選了 \(x\) 個 \(i\),那麼就有轉移:

\[f(i, j, h + (s + x) \bmod 2, \left\lfloor\frac\right\rfloor) \gets_+ f(i - 1, j - x, h, s) \cdot v_i^x \cdot \dbinom

\]最後答案即為:\(\sum\limits_(s) \leq k} f(m, n, h, s)\)。

其中 \(\text(s)\) 表示 \(s\) 二進位制表示下 \(1\) 的個數。

時間複雜度 \(\mathcal(n^4m)\)。

#include #include #include using namespace std;

inline int read()

while (s >= '0' && s <= '9')

return x * f;

}int power(int a, int b, int p)

return ans;

}const int n = 35, m = 110;

const int mod = 998244353;

int n, m, k;

int val[m];

int fact[n], inv[n];

int c(int n, int m)

int f[m][n][n][n];

void add(int &x, long long d)

int cnt[n];

int main() }}

} }for (int s = 1; s <= n; s ++)

cnt[s] = cnt[s ^ (s & -s)] + 1;

int ans = 0;

for (int h = 0; h <= n; h ++)

for (int s = 0; s <= n; s ++)

if (h + cnt[s] <= k) add(ans, f[m][n][h][s]);

printf("%d\n", ans);

return 0;

}

簡單處理可以把方差的 \(n^2\) 倍化為:

\[n\left(\sum\limits_^n a_i^2\right) - \left(\sum\limits_^n a_i\right)^2

\]那麼,只需要知道乙個序列的數值和數值平方和,就可以計算出該序列的方差了。

記差分陣列 \(b_i = a_i - a_\),那麼一次對 \(i\) 的操作就相當於交換 \(b_i, b_\)。

由於鄰項交換,並且可以進行任意多次操作。

那麼所有操作結束後的新差分陣列 \(\\) 可以是原差分陣列 \(\\) 的任意乙個排列。

乙個結論:最優解的差分陣列一定是先遞減、後遞增的(也就是單谷的)。

根據這個性質,我們就可以先將所有的 \(n - 1\) 個差分值從小到大排序,然後從小到大加入差分值。由於方差關係的是資料之間的相對大小,我們不妨欽定乙個基準點,它的值為 \(0\)。

設 \(\text_i\) 表示前 \(i\) 小的差分值之和是多少。

設 \(f(i, s)\) 表示:考慮到前 \(i\) 個差分值,當前的數值和為 \(s\) 時,數值平方和最小是多少。

若向當前序列的左端加數,則有轉移:

\[f(i, s + b_i \cdot i) \gets_\text f(i - 1, s) + 2 \cdot s \cdot b_i + i \cdot b_i^2

\]若向當前序列的右端加數,則有轉移:

\[f(i, s + \text_i) \gets_ f(i - 1, s) + \text_i^2

\]最後的答案即為 \(\min\\)。

設值域為 \(m\),直接做顯然是 \(\mathcal(n^2m)\) 的。

但是注意到整個序列 \(\\) 中不同的數總共就只有 \(\min(n, m)\) 個,也就是說差分陣列中不為 \(0\) 的有效差分值不超過 \(\min(n, m)\) 個,於是就可以優化到 \(\mathcal(\min(n, m) \cdot nm)\)。

#include #include #include using namespace std;

inline int read()

while (s >= '0' && s <= '9')

return x * f;

}const int n = 10010, sz = 500100;

const int inf = 0x3f3f3f3f;

int n, m, l, res;

int a[n], b[n];

int sum[n];

int p, q;

int f[2][sz];

int main()

sort(b + 1, b + n);

for (int i = 1; i < n; i ++)

sum[i] = sum[i - 1] + b[i];

p = 0, q = 1;

f[p][0] = 0;

for (int s = 1; s <= l; s ++) f[p][s] = inf;

for (int i = res + 1; i < n; i ++)

} long long ans = 1e18;

for (int s = 0; s <= l; s ++)

ans = min(ans, 1ll * n * f[p][s] - 1ll * s * s);

printf("%lld\n", ans);

return 0;

}

(待填 ...)

NOIP2021 棋局 題解

由於存在相對顏色大小關係,我們考慮線段樹合併來維護這個結構。為了後續的討論的方便,我們將lv進行離散化。這裡的離散化是在對統計不產生影響時,將所有的值都變得不同。對於每個連通塊我們維護四顆線段樹分別表示表示 同時維護兩個是為了之後處理直行道路時好處理。初始化時,我們將每個點的相鄰的有互信道路且沒有點...

NOIP2021 簡要覆盤 題解

luogu7961.設 f i,j,k,l 表示考慮前 i 位,放了 j 個數,字尾 k 個 1 狀壓 i 及前三位結果為 l 的權值和 無序 轉移平凡,最後分配順序即可。luogu7962.有經驗的選手很快發現等價於交換差分陣列,並不難發現差分陣列一定是單谷的,證明不會,但想一下方差反應的是 資料...

比賽遊記 NOIP2021 遊記

2021 福建省 noip 程式設計水平測試。考點在福州三中 西湖校區 一共三小時,從 9 00 考到 12 00 開啟problem.pdf一看,好傢伙,標題是 福建省 noi2011 省隊選拔賽 第一天 fjoi 先生,世間所有的相遇都是久別重逢。看完 a 題,發現好像是最短路,很快就有了乙個 ...