對於NOIP2021的全面總結
賽時:
考場拿到題目較為鎮定,直接開 T1 ,一眼秒掉了根號預處理,$30min$ 喜提 $70pts$ 的好成績。按照先暴力再正解的策略,去看 T2。讀題后有些恍惚,沒有看數據范圍直接做,覺得不太可做,看了數據范圍有 $m\leq 12$,果斷計數 DP,轉移后謹慎的判了邊界條件,過掉了 7 位數樣例后覺得 $50pts$ 穩了,哇,第一次考場上 dp 耶。T3 本來看上去非常親切,之前模擬有兩道無上限操作的題目,還有方差公式,設計了 dp 狀態,但是並不會轉移方差,想了很久無奈去寫了暴搜。T4 題面很長,由於 T3 耽誤了太多時間,T4暴力沒有太敢寫,因為一寫又要很長時間,然后怕自己調不完,於是決定先去寫 T1 正解。發現可以用類似埃氏篩法做,但怕復雜度炸掉,親測規模 $2.8\times 10^7$,但考場上把八位數看成了八次方,所以以為自己被卡常了,覺得不太好,一直在想怎么改成類似於線性篩,於是時間流逝而去,T4暴力終究沒寫無奈固輸,導致貌似有 $44pts$ 沒拿到,非常可惜。
題目分析
T1
$70pts$ 根號維護,復雜度 $O(n\sqrt n+T)$。正解類埃氏篩法,復雜度 $O(nloglogn+T)$ ?。

1 #include<bits/stdc++.h> 2 using namespace std; 3 int vis[10000005],pos,ans[10000005],T,n[200005],maxn; 4 bool check(int x) 5 { 6 while(x) 7 { 8 if(x % 10 == 7) return true; 9 x /= 10; 10 } 11 return false; 12 } 13 int main() 14 { 15 freopen("number.in","r",stdin); 16 freopen("number.out","w",stdout); 17 scanf("%d",&T); 18 for(int t = 1;t <= T;t++) 19 { 20 scanf("%d",&n[t]); 21 maxn = max(n[t],maxn); 22 } 23 if(maxn <= 200000) 24 { 25 for(int i = 1;i <= 9;i++) vis[i] = (i == 7) ? -1 : 1; 26 for(int i = 10;i <= 200002;i++) 27 { 28 if(check(i)) 29 { 30 vis[i] = -1; 31 continue; 32 } 33 int flag = 0; 34 for(int j = 1;j * j <= i;j++) 35 { 36 if(i % j != 0) continue; 37 if(vis[j] == -1 or vis[i / j] == -1) 38 { 39 flag = 1; 40 break; 41 } 42 } 43 vis[i] = flag ? -1 : 1; 44 } 45 ans[200000] = 200002; 46 pos = 200000; 47 for(int i = 199999;i >= 1;i--) 48 { 49 if(vis[i] == -1) ans[i] = -1; 50 else 51 { 52 ans[i] = pos; 53 pos = i; 54 } 55 } 56 for(int t = 1;t <= T;t++) 57 printf("%d\n",ans[n[t]]); 58 return 0; 59 } 60 for(int i = 1;i <= 10000001;i++) 61 { 62 if(vis[i] == -1) continue; 63 if(not check(i)) continue; 64 for(int j = 1;j * i <= 10000001;j++) 65 { 66 if(vis[i*j] == -1) continue; 67 vis[i*j] = -1; 68 } 69 } 70 pos = 10000001; 71 for(int i = 10000000;i >= 1;i--) 72 { 73 if(vis[i] == -1) ans[i] = -1; 74 else 75 { 76 ans[i] = pos; 77 pos = i; 78 } 79 } 80 for(int t = 1;t <= T;t++) 81 printf("%d\n",ans[n[t]]); 82 return 0; 83 }
T2
$50pts$ 設 $f_{i,j}$ 表示第 $i$ 個填完后 $S$ 值為 $j$ 的權值和,計數轉移即可,復雜度 $O(n^2m2^m)$。

1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 35; 5 const ll mod = 998244353; 6 ll f[N][150000]; 7 ll ans; 8 ll v[110]; 9 int n,m,k; 10 bool check(int x) 11 { 12 int cnt = 0; 13 while(x) 14 { 15 if(x & 1) cnt++; 16 x >>= 1; 17 } 18 if(cnt > k) return false; 19 else return true; 20 } 21 int main() 22 { 23 freopen("sequence.in","r",stdin); 24 freopen("sequence.out","w",stdout); 25 scanf("%d%d%d",&n,&m,&k); 26 for(int i = 0;i <= m;i++) scanf("%lld",&v[i]); 27 for(int i = 0;i <= n * (1 << m);i++) f[0][i] = 1; 28 for(int i = 1;i <= n;i++) 29 for(int j = 0;j <= i * (1 << m);j++) 30 for(int k = 0;k <= m;k++) 31 if(j - (1 << k) >= 0 and j - (1 << k) <= (i - 1) * (1 << m)) f[i][j] = (f[i][j] + f[i-1][j - (1 << k)] * v[k] % mod) % mod; 32 for(int i = n;i <= n * (1 << m);i++) 33 { 34 if(not check(i)) continue; 35 //cout << i << endl; 36 ans = (ans + f[n][i]) % mod; 37 } 38 printf("%lld\n",ans); 39 return 0; 40 }
T3
搜的性質沒有找好,$12pts$,別人 $20pts$。(好像掛了?)
T4
。。。,時間與暴力,我。。。,沒有看出特殊性質暴力分好拿非常可惜。
賽后
期望:$100+50+12+0=162$
實際:$100+50+0+0=150$