2022SWJTUACM新秀杯题解
0.導讀
開坑,記錄下做過的題。只會簽到捏。
題解不完全,不會,先開個坑,興許寒假打完5場比賽填坑
1.2022SWJTU月賽
M.發工資
思想:貪心地將每個元素修改為平均數,代價傳給后繼元素。若元素為平均數,(因前驅的修改變為了平均數,無需修改了)
int solve() {int ans=0;for(int i=0;i<n;i++) a[i]-=avr;for(int i=0;i<n;i++){if(a[i]) {a[i+1]+=a[i];ans++;}}return ans; }C.積水問題
-
法一.貪心分治
找出被兩個極大值包裹的區間,區間的權易求
左右區間重復如上分治操作
-
法二.排序加速
用高度的降序排列p作為輔助,維護一個t的已solve的lr區間,每次迭代入一個次大高度,拓展lr區間
int solve() {p[0].id=0,p[n+1].id=n+1;//兩側用0包裹 sort(p,p+1+n+1);int l,r;//lr最高柱重合 l=r=p[0].id;//每次ask一個較大的數,然后判斷這個數的id在那個區間,然后這段區間易求權 //0.lr維護的是已經ask過的區間 //易證內嵌for總共只會遍歷一邊數組,所以時間復雜度是O(2n) for(ri i=1;i<=n+1;i++){if(p[i].id>r){int top=min(p[i].val,t[r]);for(int j=r+1;j<p[i].id;j++) ans+=top-t[j];r=p[i].id;}if(p[i].id<l){int top=min(p[i].val,t[l]);for(int j=p[i].id+1;j<l;j++) ans+=top-t[j];l=p[i].id;}}return ans; }J.alwaysRank
題面
在二維平面里有n個得分點(xi,yi),\{R^2\}內任取一點(i,j),其得分為到這些得分點的曼哈頓距離之和。求得分最少的點的數目
曼哈頓距離定義:Manhattan(p1,p2)=|x_1-x_2|\ +\ |y_1-y_2|
解析
曼哈頓距離是的x軸與y軸是離散化的,
考查一維數軸上放置n個得分點——
若n為奇數,有且僅有中位數那個點得分最少,得分為Σx_r-x_l,l+r=n
若n為偶數,中位數區間的所有點都是得分最少的點
考查二位數軸上的積分點,考查x軸上積分最少的點與y軸上積分最少的點,顯然是col*row;
int solve() {int row=0,cow=0;sort(x+1,x+1+n);sort(y+1,y+1+n);if(n%2) row=1,col=1;else row=x[n/2+1]-x[n/2]+1,col=y[n/2+1]-y[n/2]+1;returncol*row; } 寒假打完比賽回來填坑……2.新秀杯初賽
A. 最長括號匹配
Description
給定一個只含有'('和')'字符的字符串,長度為 n(1≤n≤10^3)
。求最長有效括號子串(格式正確且連續)的長度。
包括形如(()),()()
Solution
以每一個(為可能的字串起點,往后檢查匹配長度,記錄(個數ls和)個數rs,故當rs>ls時這是一段非法的字串,當ls>rs時可以往后搜索,當ls==rs是一段可行的字串,需要記錄答案然后繼續往后搜索
Code
#include<bits/stdc++.h> using namespace std; string s; int ans=0; ? int main() {cin>>s;for(int i=0;i<s.size();i++){if(s[i]=='('){int ls=0,rs=0;for(int j=i;j<s.size();j++){if(s[j]=='(') ls++;else rs++;if(ls==rs){ans=max(ans,j-i+1);}if(rs>ls) {i=j-1;break;}}}}cout<<ans;return 0; }O(n)解法
D.救救qq
水題不貼
E.幫幫小冷
Description
小冷有一個大小為n的數組,小冷不喜歡很大的數,他想盡可能使數組的和盡可能小,現在只有一種使數組變小的辦法,若數字c為數組中每一個數的因數的話,那么便可以數組中的所有數都除以c,小冷想知道最小的數組的和,你能幫幫他嗎qwq。
Solution 區間gcd問題,可以用ST表 數據太弱只有一個查詢,On跑了,水題
#include<bits/stdc++.h> using namespace std;const int N=1e6+5,M=1e4+5; long long a[N]; int n; long long ans=0; long long by[N]; int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n;for(int i=0;i<n;i++) cin>>a[i];by[0]=a[0];for(int i=1;i<n;i++){//cout<<by[i-1]<<"\n";by[i]=__gcd(by[i-1],a[i]);}for(int i=0;i<n;i++) ans+=a[i]/by[n-1];cout<<ans;return 0; }I.西南膠帶也是膠帶啊
Description
在由于XCPC主辦方多次將SWJTU和XAJTU的物資發錯,兩校ACM校隊達成協議:將錯就錯。
現在XAJTU和SWJTU都有不超過26支隊伍。其中XAJTU有Au1支隊伍能拿金牌,Ag1支隊伍能拿銀牌,Cu1支隊伍能拿銅牌;而SWJTU有Au2支隊伍能拿金牌,Ag2支隊伍能拿銀牌,Cu2支隊伍能拿銅牌。
兩校如果拿到自己學校隊伍的物資,則令對應的隊伍參賽,在剩余的隊伍中,未拿到自己物資的隊伍由強到弱給按物資順序拿對方物資,給對方代打
假設每支隊伍都會正常發揮,獲取到其校內排名對應的獎牌。如果SWJTU的獲得金牌數量比原本能獲得的多,那么宋老板會很開心,如果獲得金牌的數量不變,而獲得銀牌的數量變多了,宋老板也會很開心,實在不行銅牌數量增多了宋老板也會很開心!
如果宋老板很開心,他就會請大家在星期四吃麥,請問大家有沒有機會在周四吃到麥。
Solution
小模擬,題面寫的真爛
Code
#include<bits/stdc++.h> using namespace std; ? int au1,ag1,cu1,au2,ag2,cu2; string xat,swt; char Upper[26]; int len=0; int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>au1>>ag1>>cu1>>au2>>ag2>>cu2;cin>>xat>>swt;int cnt=0,nau=0,nag=0,ncu=0;int au=0,ag=0,cu=0;int oau=0,oag=0,ocu=0;for(int i=0;i<xat.size();i++){//仙交摸到稀膠 if(xat[i]>='a' && xat[i]<='z'){cnt++;//稀膠自己打,o if(xat[i]-'a'<=au2) oau++;else if(xat[i]-'a'+1>au2 && xat[i]-'a'+1<=au2+ag2) oag++;else if(cnt<=au2+ag2+cu2)ocu++; //仙交代打 ,n if(cnt<=au1) nau++;else if(cnt>au1 && cnt<=au1+ag1) nag++;else if(cnt<=au1+ag1+cu1) ncu++; }} ?if(nau>oau || nag>oag || ncu>ocu) cout<<"Yeah!";else cout<<"No!";return 0; }J.最強的對手!至大的危機!
Description
與蕈獸結下堅定的羈絆!經歷多場激烈角逐后,旅行者和派蒙終于闖進了蕈獸大作戰“月蓮杯”的決賽!他們將繼承八重神子和萊依拉的信念,挑戰最強的馴獸師海妮耶,并揭穿隱藏在“月蓮杯”大賽之下的陰謀!
但在此之前,派蒙決定先與蕈獸伙伴們進行元素反應特訓...
由于派蒙不想計算元素反應中的元素附著保留概率,所以她將元素反應機制略微改變了,改變后的元素反應特訓將以如下規則展開:
已知,每種蕈獸都有一種元素屬性:
Bongo-Head(咚咚小圓帽)為Hydro(水元素)的蕈獸,Twirly-Whirly(轉轉悠悠獸)為Anemo(風元素)的蕈獸,Blitzara(百雷遮羅)為Electro(雷元素)的蕈獸。
當蕈獸釋放元素戰技,木樁會被附著上對應的元素,若木樁上早已有元素附著,將會產生元素反應,并且將木樁上的元素附著更改為當前元素。需要注意的是,風元素不會附著于木樁。元素反應如下所示:
感電 Electro-Charged :水元素-Hydro+雷元素-Electro
擴散 Swirl : 風元素-Anemo+雷元素-Electro/水元素-Hydro
在元素反應特訓中,派蒙將蕈獸伙伴們進行編隊,分別放在一號位,二號位和三號位上。接下來,她會發出一連串僅含'1','2','3'的指令,指示位于該號位上的蕈獸對木樁釋放元素戰技!
而你,旅行者,將作為特訓中的木樁!請你精準的、按順序的輸出每次元素反應的類型。若特訓中未產生元素反應,請你輸出"hehe"(不含雙引號)。
注:請遵循題面詞匯的大小寫格式。
Input
每個樣例包含多組測試數據,第一行輸入一個整數 T,代表該組樣例中測試數據的數量。
對于每組測試數據,第一行包含三個字符串,中間以空格隔開,代表蕈獸的編隊順序。
對于每組測試數據,第二行包含一個長度不小于1的,僅含阿拉伯數字'1','2','3'的字符串。
Output
對于每組測試數據,按順序輸出元素反應(以英文詞匯輸出),中間以空格隔開。若無元素反應,請輸出“hehe” (不含雙引號)。
請在每組測試數據的輸出之后添加一個換行符。
Solution
模擬,挺好的,就是不懂叩
,讀了很久題面。感電是加法不區分兩種元素的先后,擴散會使木樁失去附著元素
用map<string,char>映射指令順序op為打出的元素順序,再遍歷元素順序模擬就好了
Code
#include<bits/stdc++.h> using namespace std; map<string,char> F; int T; string rct[3]={"Swirl","Electro-Charged","hehe"}; void solve(string op) {bool flag=1;char state='0';if(op[0]!='2') state=op[0];for(int i=1;i<op.size();i++){// cout<<"ops:"<<state<<' '<<op[i]<<"\n";if(state!='0') {//擴散后沒有元素 if(op[i]=='2' && (state=='1' || state=='3')) {cout<<rct[0]<<' ';flag=0;state='0';}//感電還有元素 if((op[i]=='3' && state=='1') || (op[i]=='1' && state=='3')) {cout<<rct[1]<<" ";flag=0;//state='0';}if(op[i]!='2' && state!='0') state=op[i];//cout<<"newstate:"<<state<<"\n"; }else if(op[i]!='2') state=op[i];}if(flag) cout<<rct[2]; } int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);F["Bongo-Head"]='1';F["Twirly-Whirly"]='2';F["Blitzara"]='3';cin>>T;while(T--){string a[4],op;cin>>a[1]>>a[2]>>a[3]>>op;for(int i=0;i<op.size();i++){op[i]=F[a[op[i]-'0']];}// cout<<"op:"<<op<<'\n';solve(op);cout<<'\n';}return 0; }K.超級炸彈人
Description
炸彈人是一款經典的小游戲。在超級炸彈人中,宋老板的魔法讓炸彈得到了加強,在一個二維網格地圖上放置的任意一個炸彈將在 t秒后爆炸,并瞬間殺死同行同列的所有玩家(炸彈可以與玩家重疊)。
林老師操控的炸彈人在網格中,一秒只能往上下左右移動到相鄰的網格,請問在第 0 秒最少放置多少枚炸彈(可放置在任意位置),才能保證殺死林老師。
solution
不加證明地給出,在tony_Lin的逃生策略和炸彈的放置策略均為最優下,tony_Lin需要一直往對角線跑。其在ts內活動范圍是一個徑長2t+1的正棱形,覆蓋這個棱形需要在對角線放滿炸彈ANS:
cout<<(t/2)*2+1;G.仙人掌攻擊
Description
給出一段序列a(1<=n<=40),并有k次query給出hp,每次求其組合出∑select=hp的方案數
Solution
先給出自頂向下減而治之的遞歸
先升序排序便于考查
在考慮a[it]這株
下,有兩種狀態:選取it,考查考慮前it-1株仙人掌下組合出hp-a[it]的方案數和不選取這個仙人掌,考查考慮前it-1株仙人掌下組合出hp的方案數
//sort(a+1,a+n+1); ll dfs(ll hp,ll it) {if(hp==0 || a[it]-hp==0) return 1;if(sum[it]<hp || !it) return 0;if(it>n ) return 0;return dfs(hp-a[it],it-1) + dfs(hp,it-1);}在一冰老師的測試用例中,所有n和k都是拉滿的,且永遠無法破宋老板的防,即一般情況下會跑滿dfs時間復雜度O(2^n),n=40
所以加個前綴和剪枝就能0ms過
T-Solution:分治
by趙老板:所以可以將40個數據分為兩組,每組20個,枚舉每組組內的結果并記錄(此時需要枚舉220≈106次),分別排序后,一組從小到大,另一組從大到小枚舉,即可完成次數統計。也可以使用map、手擼二叉堆等方式實現查找。時間復雜度為O((2(n/2)*log(2(n/2)))。
我用map<ll,ll>mapr映射全組合中各個atk的方案數。
Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 45; int n, m; int a[N]; ll lhs[ (1 << 20) + 5], rhs[(1 << 20) + 5]; map<ll, ll> mapl, mapr; int lenl = 0, lenr = 0; void dfsl(ll atk, int lo, int hi) { ?if (lo == hi){return;} ?for (int i = lo + 1; i <= hi; i++){lhs[lenl++] = atk + a[i];mapl[atk + a[i]]++;dfsl(atk + a[i], i, hi);} } void dfsr(ll atk, int lo, int hi) { ?if (lo == hi){return;} ?for (int i = lo + 1; i <= hi; i++){rhs[lenr++] = atk + a[i];mapr[atk + a[i]]++;dfsr(atk + a[i], i, hi);} } int main() {ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i++) cin >> a[i];sort(a + 1, a + n + 1);for (int i = 1; i <= n / 2; i++){lhs[lenl++] = a[i];mapl[a[i]]++;dfsl(a[i], i, n / 2);}for (int i = n / 2 + 1; i <= n; i++){rhs[lenr++] = a[i];mapr[a[i]]++;dfsr(a[i], i, n);} ?for (int k = 0; k < m; k++){ll hp;cin >> hp;ll ans = 0;for (int i = 0; i < lenl; i++){if (lhs[i] == hp) ans++;elseans += mapr[hp - lhs[i]];//用映射加速右邊的查詢}for (int j = 0; j < lenr; j++){if (rhs[j] == hp) ans++;}if (hp == 0) ans++;cout << ans << '\n';}return 0; }H.宋老板與炒房
Awesome 二分答案...
Description
宋老板炒資金賠錢了,他決定把錢投資到房產市場上炒房子。
現在市場上有N(1<N<2e5)套房子,第i套房子現價為ai,3年后的預期價格為bi。(1<b<3a<1e9)
宋老板想投資其中的M套房子,宋老板很有錢,但是想讓這些錢購買的房子的 ∑3年后預期價格/∑現價 最大,請你幫助宋老板計算該比例的最大值。
因為宋老板很聰明,所以這些房子的3年后預期價格都會超過現價的,但是因為宏觀調控,3年后預期價格不超過現價的3倍
輸出一個浮點數,要求保留小數點后4位,表示賺到錢最大的比例
Solution
典型錯解是按漲價倍率排序,因為有些房子賠率高,但價值低,最終貢獻一般
考查知識點:二分答案
對于答案空間有限的題,可以通過枚舉答案檢查其是否可行,最終找到最優答案。而這種枚舉可以二分優化
本題答案空間是N=[1:10-4:3],枚舉粒度為10-4二分枚舉效率為O(long(n)),
枚舉元素check方案:對于所枚舉的倍率p,每套房子現價為a,預期價格為b。其貢獻價值為b-ap。
最優解一定可行,但可行解不一定最優。我們假設整個解序列具有單調性,且一個數x為可行解,那么一般的,所有的x'(x'<x)都是可行解。并且,如果有一個數y是非法解,那么一般的,所有的y'(y'>y)都是非法解。
在這里,若sum>0,在p這個選擇方案下的解都大于這個p,最優解一定大于這個p,p一定是非法解,搜索p右邊的解
若sum<=0,我們的選擇方案下的解<=p,搜索p左邊的解
Code
#include<bits/stdc++.h> using namespace std; const int N=2e5+5,M=2e5+5; int a[N],b[N]; int n,m; const double er = 1e-4; ? double c[N]; struct node {double val;int id;}; bool cmp(const node& lhs,const node& rhs) {return lhs.val>rhs.val; } double suma=0,sumb=0; void binary (double l,double r) {vector<node> c(n);//左閉右閉搜搜while(r-l>er){double p=(l+r)/2;// cout<<p<<" ";//ap-b的意義:這套房子對倍率p的貢獻,ap-b>0說明這套房子的倍率小于p,反之大于p //故優先選取ap-b低的房子,若組合出的sum>=0,說明倍率p難以達成,ans<=p //反之倍率p可達成,向右搜索更有參考意義的p//ans是小于二分答案p的最大值 for(int i=0;i<n;i++){c[i].val=b[i]-a[i]*p;c[i].id=i;// cout<<c[i].val<<" ";}sort(c.begin(),c.end(),cmp);double sum=0;for(int i=0;i<m;i++) sum+=c[i].val;// cout<<sum<<" ";if(sum<=0){suma=0,sumb=0;for(int i=0;i<m;i++) suma+=a[c[i].id],sumb+=b[c[i].id];r=p;}else{l=p;}// cout<<'\n';} } int main() {ios::sync_with_stdio(false);cin>>n>>m;for(int i=0;i<n;i++) cin>>a[i];for(int i=0;i<n;i++) cin>>b[i];binary(1,3+er);printf("%.4f",sumb/suma);return 0; }看看ap-b分布:
可知按ap-b排列不等于按b/a排列
新秀杯決賽
Awesome//(ㄒoㄒ)/~~
A.裝修
Description
給定矩形size為N(5e3)和M(1e5)次操作,每次為一個矩形區域Area(a,b,c,d)加上一個數h,最后查詢矩形內最大值和最大值出現次數。
Solution:二維差分:將矩陣a按行分塊,每次矩形加法拆解為(c-a)個行加法,在每一個行向量左端加上差分,右端減去差分即可,進一步,每一次操作都是對同一列的位置作差分,可以再優化出二維差分。
矩陣a的差分策略:
//按行對矩陣a分塊,對于區間操作plus(a,b,c,d,h),將左列的del加h,將右列的del-h,,做列前驅差分。前綴和反解a策略為左元素加此差分 for(int i=a;i<=c;i++) {del[i][b]+=h;del[i][d+1]-=h; } a[i][j]=a[i][j-1]+del[i][j];del的差分策略
//對一列del+h的差分策略:上端點+h,下端點-h ddel[a][b]+=h; ddel[c+1][b]-=h;ddel[a][d+1]-=h; ddel[c+1][d+1]+=h; ? del[i][j]=del[i-1][j]+ddel[i][j]至次算法時間復雜度為O(m + n^2 ),可過
考查空間復雜度,開了三個N^2 數組,O(3n^2)=3*(5e3)^2*4/2^6=286MB
考查優化1:用動態數組管理ddel,del,和a,同一時間只需要兩個數組
會tle的,因為盡管是O(n^2)=2.5e7級別,內存管理仍是很大的時間開銷,會t
優化策略:del可以原地求前綴和
考查數據范圍:a[i].maxn=1e5*1e5=1e10,大于int與unsigned int
在windows下sizeof(long)=4,就是int啦,所以要開long long
考查同時開long long a[N][N]與int p[N][N],空間復雜度O(2n^2)=1*(5e3)^2*4+1*(5e3)^2/2^6=286MB
最終內存管理策略:每用一行del開始構造一行a,malloc一行a,每構造完畢一行a,free一行del (malloc比new快一丟丟?
AC代碼資源消耗:
| 646ms(主要消耗為n^2下malloc) | 194MB |
Code
#include<iostream> #include<stdlib.h> using namespace std; const int N=5005,M=100005; typedef long long ll; int n,m; ll ans=0; int mey; ? int* del[N]; ll* a[N]; int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=0;i<=n+1;i++){del[i]=(int*)malloc(sizeof(int)*(n+2));for(int j=0;j<=n+1;j++) del[i][j]=0;}while(m--){int a,b,c,d,h;cin>>a>>b>>c>>d>>h;del[a][b]+=h;del[c+1][b]-=h; ?del[a][d+1]-=h;del[c+1][d+1]+=h;}//不懂,原地求前綴和吧 for(int j=1;j<=n;j++){for(int i=1;i<=n;i++){del[i][j]+=del[i-1][j];// cout<<del[i][j]<<" ";}// cout<<'\n';} // cout<<'\n';for(int i=1;i<=n;i++){a[i]=(ll*)malloc(sizeof(ll)*(n+1));a[i][0]=0;for(int j=1;j<=n;j++){a[i][j]=a[i][j-1]+del[i][j];// cout<<a[i][j]<<" ";}free(del[i]);// cout<<'\n';}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(a[i][j]>ans){ans=a[i][j];mey=0;}if(a[i][j]==ans) mey++;}free(a[i]);}cout<<ans<<" "<<mey;return 0; }可以指出,頻繁的new與delete,對資源的消耗是災難性的(在O(n^2)=2.5e7下tle),還會有碎片化,內存泄露的問題
Reference:
1.new vs malloc
2.delete vs free
3.ptmalloc
4內存管理方案
C.幸運農場Ⅰ
Description
宋老板有一片n行m列大小的農場(n*m<1e5),里面種有若干白菜。如圖所示,在農場的每一格至多只有一顆白菜
求同行或同列或同斜線的白菜的對數
Solution
有四個直線系:i=C,j=C,i+j=C,i-j=C
因為C因為可能是負,用四個map來記錄這個各個線系的白菜的個數,ans為\sum線系的C_n^2
Code
簽到題不貼
若數據強度是(n,m<1e5)呢? 胸牌哥說拿頭做。O(n^2)的IO捏
G.宋老板與序列
Description
給定A=\{a_1,a_2,...,a_n\},B=\{b_1,b_2,...,b_n\}兩條1-n的序列
當|a_{i}-a_{j}|=i∣或者|a_{i}-a_{j}|=j∣時,可將a_{i}與a_{j}交換
問:能否進行若干次交換(包括00次)將序列AA變為序列BB
Solution
不懂證明地給出,一定可完成變換
Code
cout<<"YES"; 6 1 4 2 3 5 7 →6 1 4 2 7 5 3 2 1 4 6 3 5 7 1 4 5 6 3 2 7 3 4 2 1 5 6 7 4 3 2 1 5 6 7 1 2 3 4 5 6 7將a元素置于i位置,他就可以與a+i或a-i或 a=b+j交換
應該,也許,總能恢復為自然序列
H.宋老板與字符串
Description
對一段字符串s循環操作如下:
for 1:strlen(s)reverse(s(1,i));打印最后字符串
Solution
不加證明地給出,就是這個字符串從后往前隔項遍歷,再從前往后隔項遍歷
K.STTTTL
Description
請編程實現以下兩個操作:
操作1:向可重集合種添加一個整數x,格式為:1 x
操作2:從小到大輸出可重集合中恰好出現yy次的整數,格式為:2 y
若集合中沒有恰好出現yy次的整數則輸出-1
Solution
multiset
總結
以上是生活随笔為你收集整理的2022SWJTUACM新秀杯题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年高考成绩查询湖南电信,湖南省通
- 下一篇: @click.stop作用(阻止点击事件