A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|
\(\checkmark\) | \(\checkmark\) | \(O\) | \(\checkmark\) | \(\checkmark\) | \(O\) | \(\checkmark\) | \(O\) | \(O\) | \(\times\) |
\(\checkmark\):表明比賽時經過。node
\(O\):表明賽後補題經過。c++
\(\times\):表明目前還未經過。shell
題目連接數組
在一個\(n\)行\(m\)列的矩陣點陣中求出知足一下要求的三角形的個數:spa
三角形的面積爲\(1\),而且橫縱座標均爲整數,那麼分爲兩種狀況(平行\(x\)軸或者\(y\)軸的爲底邊):code
再根據底邊平行的軸不一樣,分爲四種狀況便可。排序
#include<bits/stdc++.h> const int mod = 1e9+7; const int maxn=1e5+10; typedef long long ll; using namespace std; int main() { ll y,x; cin>>y>>x; ll res; if(x>=3) res = ((2LL*(y-1)*(x-2))%mod)*(x-2+y)%mod; if(y>=3) res+= ((2*(x-1)*(y-2))%mod)*(x+y-2)%mod; res%=mod; cout<<res<<endl; }
規律總結題。ci
題目連接字符串
每一個音符有\(x\%\)的機率得\(a\)分,有\((100-x)\%\)的機率得\(b\)分,求\(n\)個字符的得分指望。get
求出\(n\)個字符分爲所有爲\(a\)分和\(b\)的得分,乘以對應的機率即爲答案。
#include<bits/stdc++.h> const int maxn=1e5+10; typedef long long ll; using namespace std; int main() { double n,x,a,b; cin>>n>>x>>a>>b; a*=n; b*=n; a*=x; a/=100; b*=(100-x); b/=100; printf("%.2f",a+b); return 0; }
簽到題,但因爲本身的粗心,沒有注意浮點數的使用。在比勝過程中遇到除運算的時候,應當當心一點。
在二維座標中有一個起始點\((x_0,y_0)\)與其餘\(n\)個點相連構成\(n\)條射線,在\(x\)軸或者\(y\)軸上放一個擋板,切斷兩點之間的鏈接,現求擋板的最小長度以使沒有被切斷的連線的數量不超過\(k\)
\(n\)個點中與起始點在同一象限的點是不可能被擋板給擋住的,那麼分別統計與起始點不在同一象限的點與起始點的連線在\(x\)軸和\(y\)軸的交點。題目求沒有被擋板擋住的連線不超過\(k\),換言之就是要擋住至少\(n-k\)個點。而後找出連續\(n-k\)個點構成區間的最小值。
#include<bits/stdc++.h> const int maxn=1e5+10; typedef long long ll; using namespace std; vector<double>v1,v2; int main() { double x0,y0; cin>>x0>>y0; int n,k; cin>>n>>k; k=n-k; for(int i=0;i<n;i++){ double x1,y1; cin>>x1>>y1; double a=(y0-y1),b =(x1-x0); if(x1*x0<0){ double jiao=a/b*x0+y0; v2.push_back(jiao); } if(y1*y0<0){ double jiao=x0+b/a*y0; v1.push_back(jiao); } } sort(v1.begin(),v1.end()); sort(v2.begin(),v2.end()); double res=1e18; if(v1.size()>=k){ int st=0,ed=st+k-1; while(ed<v1.size()){ res=min(res,v1[ed]-v1[st]); st++,ed++; } } if(v2.size()>=k){ int st=0,ed=st+k-1; while(ed<v2.size()){ res=min(res,v2[ed]-v2[st]); st++,ed++; } } if(res==1e18)cout<<"-1"<<endl; else printf("%.7lf",res); return 0; }
有\(1,2,3 \cdots,n\)個數字,如今從中任意拿走一個數字,根據剩下的\(n-1\)個數字,判斷拿走的數字是多少。
方案1:直接排序
將輸入的\(n-1\)個數字存在一個數組當中,排一個序,遍歷數組,若是數字的下標和數字不相等,即爲答案。
方案二:求和
在輸入的過程當中求出\(n-1\)個數字的和,再根據高斯公式求出\(n\)個數字的和,兩個相減即爲答案。
#include<bits/stdc++.h> const int maxn=1e5+10; typedef long long ll; using namespace std; int a[maxn]; int main() { int n; cin>>n; for(int i=0;i<n-1;i++){ cin>>a[i]; } sort(a,a+n-1); for(int i=0;i<n;i++){ if(a[i]!=i+1){ cout<<i+1<<endl; break; } } return 0; }
簽到題。
給你一個式子\(f(x)\)表示\(x\)的正整數因子的個數,不斷迭代\(f(x)\)的結果,求最終結果爲\(2\)時的迭代次數。
直接模擬題意迭代便可,剛開始拿到這道題的時候,覺得有什麼規律,因此一開始的方向就錯了。
時間複雜度爲\(O(\sqrt{n})\)。
#include<bits/stdc++.h> const int maxn=1e5+10; typedef long long ll; using namespace std; int solve(ll x){ int cnt=0; for(ll i=2;i*i<=x;i++){ if(x%i==0){ cnt+=2; if(i*i==x)cnt--; } } return cnt; } int main() { ll n; cin>>n; int res=0; ll mid=solve(n)+2; while(mid!=2){ res++; mid=solve(mid)+2; // cout<<mid<<endl; } res++; cout<<res<<endl; return 0; }
在作題的過程當中必定要重視計算時間複雜度,最開始覺得直接模擬會炸,因此沒有去寫,往找規律的方向去思考去了,耽誤了時間。同時要注意數據的範圍,這道題就犯了這個錯誤。
在一個有\(n\)個點的樹上,每一個點被標記爲白色或者黑色,問有多少條只包含一個黑點的簡單路徑。
簡單路徑只有兩種狀況下會包含一個黑點:
這道題我的認爲切入點爲黑點,由於路徑中只有一個黑點,那麼這個黑點要麼爲起始或者重點,要麼將多個白點鏈接起來做爲中間點。這樣一來就要找到黑點鏈接的白點所在的聯通塊總共有多少個白點,由於是在樹上,因此每一個聯通塊是各自獨立的,不存在重複計算的狀況。計算聯通塊中節點的個數能夠用並查集,並查集中在鏈接兩個節點的時候,統計其所在父親的孩子數量,這樣其餘節點所在聯通塊的節點數量爲其父親節點的孩子數量加\(1\)。
假設某一個黑點,鏈接了\(k\)個節點,其中\(f(i)\)表示第\(i\)個節點所在聯通塊的節點個數:
#include<bits/stdc++.h> const int maxn=1e5+10; typedef long long ll; using namespace std; int n; string color; vector<int>G[maxn]; int pre[maxn]; void init(){ for(int i=0;i<maxn;i++){ pre[i]=i; } } int childnum[maxn]; int nodenum[maxn]; int find(int x) { int r=x; while(r!=pre[r]){ r=pre[r]; } int i=x,j; while(i!=pre[i]){ j=pre[i]; pre[i]=r; i=j; } return r; } void uni(int x,int y){ int fx=find(x),fy=find(y); if(fx!=fy){ pre[fx]=fy; childnum[fy]+=childnum[fx]+1; } } ll sum[maxn]; ll solve(vector<int>temp){ ll res=0; for(int i=0;i<temp.size();i++){ res+=temp[i]; } //求前綴和 for(int i=0;i<temp.size();i++){ sum[i+1]=sum[i]+temp[i]; } for(int i=1;i<temp.size();i++){ res+=temp[i]*sum[i]; } return res; } int main() { // freopen("data.txt","r",stdin); cin>>n; cin>>color; //對並查集數組進行初始化 init(); for(int i=0;i<n-1;i++){ int x,y; cin>>x>>y; G[x].push_back(y); G[y].push_back(x); if(color[x-1]=='W'&&color[y-1]=='W'){ uni(x,y); } } ll res=0; for(int i=1;i<=n;i++){ nodenum[i]=childnum[find(i)]+1; } for(int i=1;i<=n;i++){ if(color[i-1]=='B'){ vector<int>temp; for(int j=0;j<G[i].size();j++){ if(color[G[i][j]-1]=='W') temp.push_back(nodenum[G[i][j]]); } res+=solve(temp); } } cout<<res<<endl; return 0; }
本覺得本身對並查集的掌握比較牢靠,但這道題仍是沒有作的出來,唉!這道題用並查集來作的思惟仍是比較獨特。但願本身多多積累經驗。
給你一個只包含小寫字母的字符串,求知足有\(k\)個相同字母的子串的最小長度。
利用二維數組,分別統計\(26\)種字母的位置。若是每種字母的個數都小於\(k\),那麼就不存在這樣的字符串,輸\(-1\)。
對於每一種字母的狀況,遍歷每一行,\(i\)下標對應的值表示第\(1\)個字母出現的位置,\(i+k-1\)下標對應的值表示第\(k\)個字母出現的位置,維護區間最小值便可。
#include<bits/stdc++.h> const int maxn=1e5+10; typedef long long ll; using namespace std; vector<int>a[30]; int main() { int n,k; cin>>n>>k; string str; cin>>str; for(int i=0;i<str.size();i++){ a[str[i]-'a'].push_back(i); } int maxlen=0; for(int i=0;i<26;i++){ int len = a[i].size(); maxlen=max(len,maxlen); } if(maxlen<k){ cout<<"-1"<<endl; return 0; } int res=2e5+10; // cout<<a[1][0]<<" "<<a[1][1]<<endl; for(int i=0;i<26;i++){ if(a[i].size()<k)continue; for(int j=0;j<a[i].size()&&j+k<=a[i].size();j++){ res = min(res,a[i][j+k-1]-a[i][j]+1); } // cout<<res<<endl; } cout<<res<<endl; return 0; }
簡單的統計,沒有什麼難度。
給你一個長度爲\(n\)只包含\(01\)字符的字符串,擁有\(k\)次操做將字符\(0\)變爲\(1\)或者將\(1\)改變爲\(0\),問通過最多\(k\)次操做(\(k\)次機會能夠不用完)以後字符相同的子串的最長長度。
整體思路是貪心。先考慮\(k\)大於等於\(1\)的個數或者大於\(0\)的個數的狀況,那麼結果就是字符串的長度;另外一種狀況則統計每個\(1\)的前綴\(1\)和後綴\(1\)的位置,而後遍歷一遍,以\(start\)爲起點,\(end\)爲終點,貪心\(k\)個1的位置,將這\(k\)個\(1\)都改變爲\(0\),那麼字符串區間爲爲下標\(end+1\)的值減去下標\(start-1\)的值再加\(1\)。
/* H題補題 */ #include<bits/stdc++.h> using namespace std; int main() { int n,k; cin>>n>>k; string str; cin>>str; vector<int>v[2]; v[0].push_back(-1); v[1].push_back(-1); for(int i=0;i<str.size();i++){ if(str[i]=='0') v[0].push_back(i); else v[1].push_back(i); } int res=0; if(v[0].size()-1<=k||v[1].size()-1<=k)res=n; for(int i=0;i<2;i++){ for(int j=1;j<v[i].size()&&j+k<=v[i].size();j++){ res=max(res,v[i][j+k]-v[i][j-1]-1); } } cout<<res<<endl; return 0; }
最開始在作這道題的時候想的太過於複雜,想到用動態規劃去作,沒有往貪心上靠。
給你一個長度爲\(n\)的字符串,其中"nico" 計\(a\)分,"niconi" 計\(b\)分,"niconiconi" 計\(c\)分,求出字符串最多能得多少分。(已經計算過的字符不能重複進行計算)
利用動態規劃的思想,\(dp[i]\)表示前\(i\)個字符的最大值,轉移方程爲:
\[ \begin{align} &if(i>=3\&\&substr(i-3,4)=nico)dp[i]=max(dp[i],dp[i-3]+a)\\ &if(i>=5\&\&substr(i-5,6)=niconi)dp[i]=max(dp[i],dp[i-5]+b)\\ &if(i>=9\&\&substr(i-9,10)=niconiconi)dp[i]=max(dp[i],dp[i-9]+c)& \end{align} \]
#include<bits/stdc++.h> const int maxn=3e5+10; typedef long long ll; using namespace std; ll dp[maxn]; int main() { int n,a,b,c; cin>>n>>a>>b>>c; string str; cin>>str; int len=str.size(); dp[0]=0; for(int i=1;i<len;i++){ dp[i]=dp[i-1]; if(i>=3&&str.substr(i-3,4)=="nico") dp[i]=max(dp[i],dp[i-3]+a); if(i>=5&&str.substr(i-5,6)=="niconi") dp[i]=max(dp[i],dp[i-5]+b); if(i>=9&&str.substr(i-9,10)=="niconiconi") dp[i]=max(dp[i],dp[i-9]+c); } cout<<dp[len-1]<<endl; return 0; }
動態規劃仍是本身的弱點啊,根本沒有想到這方面,其實理解以後仍是蠻簡單的,但就是當時看到這道題過題人數不是不少,給本身形成了心理壓力,先入爲主。