2020.11.24清北課堂訓練賽題解報告
阿新 • • 發佈:2020-11-25
難易程度
依舊是一場菜雞受虐賽,盡力了QAQ,今天\(dp\)一大堆,只\(A\)掉了一個題目,其他題目該\(WA\)就\(WA\),該\(0\)就\(0\)
得分情況
T1 30分
T2 100分
T3 0分
T4 0分
(什麼時候我後倆題可以得點分)
題目分析
T1 \(diyiti\)
題目分析
30pts 爆搜
#include<iostream> #include<cstdio> #include<cmath> #define int long long using namespace std; const int N=1e5+9; int f[10000009]; int v[N],w[N]; int vis[N]; int n,k; int res,ans; void dfs(int num,int val) { if(num>k)return; ans=max(val,ans); for(int i=1;i<=n;i++) { if(!vis[i]) { vis[i]=1; dfs((num|w[i]),val+v[i]); vis[i]=1; } } } signed main() { cin>>n>>k; int va=0,wa=0; for(int i=1;i<=n;i++) { cin>>w[i]>>v[i]; if(i==1) wa=w[i]; else wa|=w[i]; va+=v[i]; } if(wa<=k) { cout<<va<<endl; return 0; } dfs(0,0); cout<<ans<<endl; return 0; }//30pts
100pts
- 比\(k\)小可以分成若干類,列舉第一位不同的地方,接下來就要求的是某一個數的子集
- 然後能選就選,複雜度 \(O(n \times 30)\)
T2 \(dierti\)
題目分析
- \(DP\),首先依次列舉序列,設\(f[i]\)為前\(i\)個數產生最長不互斥子序列的元素個數
- 然後在設定一個量瞅一瞅最大值,簡單記為\(bige_i\)表示記錄\(f[i]\)的最大值。然後我們可以得到狀態轉移方程為
剪枝優化
- 顯然的是,我們可以看到上面的情況是一種\(O(n^2)\)
100pts程式碼
#include<cstdio> #include<queue> #include<stack> #include<map> #include<algorithm> #include<iostream> #include<cmath> using namespace std; const int N=2e5+9; int f[N],bige[N];//dp轉移方程,最大值 int num[N]; int main() { int n; cin>>n; for(int i=1;i<=n;++i) scanf("%d",&num[i]); for(int i=1;i<=n;++i) f[i]=1; for(int i=1;i<=n;++i) { for(int j=i-1;j;--j) { if(bige[i-1]<f[i]) break; //說明已經找到了,因為前一個位置的最大值肯定小於後一個位置已經找到的狀態 //減少無用狀態 if(num[i]&num[j]) f[i]=max(f[j]+1,f[i]); //如果這兩個可以湊一塊,看一看加上會不會更優 } bige[i]=max(bige[i-1],f[i]);//記錄一下最大值 } cout<<f[n]<<endl; return 0; }//100pts
T3 \(dierti\)
題目分析
- 考慮\(dp\) 可以先考慮方案數而不是權值和以方便思考,實際上兩者轉移是類似的
- 設\(f(i,l,r)\)表示只考慮第\((l,r)\)這些行,第\(i\)列以後的這部分的方案數,對於一個固定的\(i,l,r\),顯然可以再\(dp\)輔助計算,\(dp(k,c)\)表示考慮完前\(k\)行,當前這一位最大填了\(c\),每一次轉移加一段\(c+1\),利用\(f(i+1)\)的資訊來轉移。
- 最暴力的做法,複雜度為\(O(10mn^4)\)
剪枝優化
- 考慮優化,很顯然的對於每一個 \(l\),不同的\(r\)的內層\(dp\)幾乎都是一樣的,放一起做就好了,本質就是內層的\(dp\)只需要先美劇\(i,l\)即可計算,複雜都為\(O(10mn^3)\)
最後注意轉移的時候複雜度不要寫殘了。權值和這個也一樣,再設一個\(g(i,l,r)\)轉移類似同上
程式碼實現(打不出來,超出理解範圍了\(QAQ\))
T4 \(disiti\)
題目分析(快饒了我吧,\(QAQ\),腦袋快離開自己了)
- 首先我們這個分裂的過程複雜,不方便考慮,所以我們就考慮每一個石子,而不是每一堆石子,一次操作相當於給每個石子分配一個\(01\)標號,那麼滿足每次標記\(1\)的石子不超過\(m\)個,這樣我們每個石子對應著一個等長的\(01\)串,我們的目標就是要讓這個\(01\)串兩兩不同。
- 我們可以二分答案,考慮如何判定某一個\(mid\),是否合法,一個的必要條件是\(mid\times m\)要足夠大,即存在一組方案使得\(01\)串兩兩不同且\(1\)的個數\(≤ mid\times m\),事實上這個也是充分的,我們給出構造來說明這一點,首先一個貪心是按照\(1\)的個數從小到大放的,顯然\(1\)的個數不是最大的那些串的\(1\)並不是平均分配的,那麼問題轉化成了要取若干個\([1,n]\)的\(m\)元子集,使得每個數出現的次數的極差\(≤1\)。我們隨便構造一組解,如果不合法那麼久找出一個出現次數太大的位置\(i\),一個出現次數太小的位置\(j\),顯然存在一個\(01\)串滿足\(s[i]=1,s[j]=0\),並且不存另一個\(01\)串\(t\)滿足\(t\oplus s=2^i\oplus 2^j\)。因此我們只需要\(swap(s[i],s[j])\).迭代顯然可以優先次結束。
- 具體實現需要求組合數,這裡不能取模但是由於\(n≤10^9\)所以直接寫不會爆$long long $.組合數增長是很快的所以暴力的複雜度可以接受
程式碼實現 (猝)
賽後總結
今天感覺比賽已經進入了自己達不到的領域,前兩個題推起來也吃力,後面的題幾乎沒有思路,連暴力也打不出來,不過從這次考試中可以很好的看出動態規劃的妙用,對自己而言,寧願寫動規也不願意打一個搞人心態的大暴力,每一次考試都可以看出自己薄弱的地方,這次反應出來的是\(dp\)和二進位制不熟練
知識點彙總
- T1 二進位制
- T2 位運算,\(DP\)
- T3 困難區間\(DP\)
- T4 組合數學,二分答案,腦子;
悲慘完結QAQ