TIps: 1.所有代碼中博主使用了scanf和printf作為輸入輸出
2.代碼中使用了define LL long long 所以在聲明變量的時候 LL其實就等價於long long
希望這兩點不會成為讀者看代碼時候的障礙qwq 另外題目鏈接我會放在最后 如果需要請往下拖一拖
T1
這道題其實就是單純對數據進行排序,但是因為關鍵字不止一種(解題數m和罰時t) 並且輸出的是隊伍的名字(也就是序號)
而正常排序完之后我們就會發現 我們並不知道當前各個位置的數對應的隊伍名是什么了(也就是其原本的位置信息已經在排序過程中丟失了)
這個時候我們發現 一個隊伍有三種信息 解題數 罰時 以及隊伍名 我們需要把這三種信息作為一個整體再將這個整體按照內部的信息(解題數以及罰時)進行排序
這個時候我們就需要用到結構體了 也就是這個東西
然后利用sort函數將結構體進行排序 但是這里我們需要自定義一個比較函數 按照自己的想法將結構體進行排序 這也就是代碼中的cmp函數的作用
這里用到了三目運算符 不了解的同學可以百度一下他的用法 這里的cmp可以理解為先比較a和b的解題數,解題數多的在前,解題數一樣再比較罰時,罰時多的在后
這樣之后通過sort函數進行排序再按順序輸出id就可以解決問題了! 復雜度Ο(nlogn)
貼一下代碼

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #define LL long long using namespace std; const int M=1e5+7; int T,n; struct node{int id,k,w;}e[M];//k表示解題數w表示罰時id表示隊伍名 bool cmp(node a,node b){return a.k==b.k?a.w<b.w:a.k>b.k;}//自定義的比較函數 int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++){ e[i].id=i; scanf("%d %d",&e[i].k,&e[i].w); } sort(e+1,e+1+n,cmp);//排序 for(int i=1;i<=n;i++) printf("%d ",e[i].id); puts("");//puts("")作用只是換行 } return 0; }
T2
這道題的話 我的做法和題解中的做法一樣qwq 師兄講的也非常清楚 這里就直接引用了
復雜度 Ο(n^2)
貼一下代碼

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #define LL long long using namespace std; const int M=1e6+7; int T,n,l,r; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); puts("YES"); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ int now=(j-1)*n+(i+j-1<=n?i+j-1:i+j-1-n); printf("%d ",now); } puts(""); } } return 0; }
T3
這道題很明顯貪心是不行的 (對於這一題來說,貪心是指我們只考慮是從i層利用電梯到第i+1層,或者單純走樓梯上去,然后對於每一層把二者取最優解傳遞給上一層)
因為很顯然我們可以一口氣坐好幾層的電梯QAQ
所以對於每一層i,我們還要記錄我們是否還在電梯井內
其實這個問題是典型的動態規划(DP)不懂的同學可以了解一下 這里也給各位提供一下比較好的博客
入門的話一般會先看背包問題 https://blog.csdn.net/weixin_41162823/article/details/87878853
而與這道題類似的DP經典題目 如
導彈攔截 https://www.luogu.com.cn/problem/P1020
最長上升子序列 https://www.luogu.com.cn/problem/P3902
題解的話洛谷都有 不會可以康康
回到這道題本身 我們用ans[i][0],ans[i][1]分別表示到達第i層的最短時間
0表示上一層是坐電梯上來的 也就是這一層要坐電梯的話就不用等待了
1表示上一層是走路上來的 這一層如果要坐電梯就需要等待了
那么我們的轉移方程就可以表示為
ans[i][0]=min(ans[i-1][0],ans[i-1][1])+b[i]
ans[i][1]=min(ans[i-1][0]+m,ans[i-1][1])+a[i]
最后輸出min(ans[n][0],ans[n][1])問題就解決了qwq
復雜度 O(n)貼一下代碼

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #define LL long long using namespace std; const int M=1e6+7; int T,n,m; LL a[M],b[M],ans[M][2];//0不坐1坐 int main(){ scanf("%d",&T); while(T--){ scanf("%d %d",&n,&m); n--; for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=n;i++) scanf("%lld",&b[i]); ans[1][0]=b[1]; ans[1][1]=a[1]+m; for(int i=2;i<=n;i++){ ans[i][0]=min(ans[i-1][0],ans[i-1][1])+b[i];//這一層走路去下一層 ans[i][1]=min(ans[i-1][0]+m,ans[i-1][1])+a[i];//這一層坐電梯去下一層 } printf("%lld\n",min(ans[n][0],ans[n][1])); } return 0; }
T4
這道題可以理解為利用容斥原理實現吧
首先我們要明確 CAT子序列雖然不用連續 但是相對順序是不能變的 也就是形如”TAC“是不符合喵喵字符串的定義的
那么我們考慮維護 C[i],A[i],T[i],CA[i],AT[i],CAT[i]六個數組
C[i]表示從1到i一共有幾個C字符 A[i],T[i]同理 也就是維護一下前綴和
CA[i]就是維護1到i位置一共有多少種CA的組合 例如一個字符串CCAA 那么CA[4]=4
AT[i],CAT[i]同理
這樣之后 對於每一個詢問l r
答案 ans=CAT[r]-CAT[l-1]-CA[l-1]*(T[r]-T[l-1])-C[l-1]*(AT[r]-AT[l-1]-A[l-1]*(T[r]-T[l-1]));
CAT[l-1]指的是1到l-1的CAT串 這種位置的串明顯是不合法的
CA[l-1]*(T[r]-T[l-1])指的是用 1 到 l-1 的“CA”和 l 到 r 的T組合而成的“CAT” 這也是不合法的子序列
C[l-1]*(AT[r]-AT[l-1]-A[l-1]*(T[r]-T[l-1]))指的是用 1 到 l-1 的“C”和 l 到 r 的“AT”組合而成的CAT 這明顯也是不合法的子序列
除去所有的不合法串后我們便可以得到答案了 算法復雜度O(n)

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #define LL long long using namespace std; const int M=2e6+7; int len,m; LL C[M],A[M],T[M],CA[M],AT[M],CAT[M]; char s[M]; int main(){ scanf("%d %d",&len,&m); scanf("%s",s+1); for(int i=1;i<=len;i++){ C[i]=C[i-1]; A[i]=A[i-1]; T[i]=T[i-1]; if(s[i]=='C') C[i]++; else if(s[i]=='A') A[i]++; else T[i]++; } for(int i=1;i<=len;i++){ CA[i]=CA[i-1]; AT[i]=AT[i-1]; if(s[i]=='A') CA[i]+=C[i]; if(s[i]=='T') AT[i]+=A[i]; } for(int i=1;i<=len;i++){ CAT[i]=CAT[i-1]; if(s[i]=='T') CAT[i]+=CA[i]; } int l,r; for(int i=1;i<=m;i++){ scanf("%d %d",&l,&r); LL ans=CAT[r]-CAT[l-1]-CA[l-1]*(T[r]-T[l-1])-C[l-1]*(AT[r]-AT[l-1]-A[l-1]*(T[r]-T[l-1])); printf("%lld\n",ans); } return 0; }
T5
這道題確實算是最難的題目了 大部分同學暫時可以跳過 以后再回來寫這道題會容易很多
所以繼續往下看的同學我會理解為你是有一定圖論基礎的
這道題看起來是一道最短路問題 但是我們但我們發現只要每個 l[i] 足夠小 r[i] 足夠大,那么就可以成為一張完全圖 這樣無論是dijkstra還是spfa都會超時
我們先按照dijkstra的想法開始解決問題 但是我們發現 因為一點個去到其他任何一個他所能到達的點的距離都是一樣的
那么我們將他丟進優先隊列的時候 可以將他離1點的距離dis[i]加上他本身的c[i] 一起丟進優先隊列中 那么每次從優先隊列里拿出來的點就一定是當前所能更新的離1最近的點了
這樣一來每個點只需要被更新一次就一定是最優解了 那么我們可以利用set維護當前未被更新的點 如果他被更新就將他從set中刪除
這樣就可以避免多次訪問同一個點 復雜度就降下來了 因為每個點只會被訪問到一次 所以復雜度是O(nlogn)
貼一下代碼

#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<queue> #define LL long long using namespace std; const int M=2e6+7; LL read(){ LL ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();} return ans*f; } LL T,n,w[M],ans[M],l[M],r[M]; struct node{ LL id,d; bool operator <(const node&x)const{return x.d<d;} }; priority_queue<node>q; set<LL>S; set<LL>::iterator L,R,C; int main(){ T=read(); while(T--){ while(!q.empty()) q.pop(); S.clear(); n=read(); for(int i=2;i<=n;i++) ans[i]=-1,S.insert(i); for(int i=1;i<=n;i++) l[i]=read(); for(int i=1;i<=n;i++) r[i]=read(); for(int i=1;i<=n;i++) w[i]=read(); ans[1]=0; q.push((node){1,w[1]}); while(!S.empty()&&!q.empty()){ node x=q.top(); q.pop(); if(l[x.id]>r[x.id]) continue; L=S.lower_bound(x.id+l[x.id]); R=S.upper_bound(x.id+r[x.id]); while(L!=R){ ans[*L]=x.d; q.push((node){*L,x.d+w[*L]}); C=S.upper_bound(*L); S.erase(L); L=C; } L=S.lower_bound(x.id-r[x.id]); R=S.upper_bound(x.id-l[x.id]); while(L!=R){ ans[*L]=x.d; q.push((node){*L,x.d+w[*L]}); C=S.upper_bound(*L); S.erase(L); L=C; } } for(int i=1;i<n;i++) printf("%lld ",ans[i]); printf("%lld",ans[n]); puts(""); } return 0; }
當然對於不會STL的同學 我們也可以考慮利用並查集代替set的作用 也就是每次更新完一個點就將他指向右邊的點(與右邊的點合並)這樣下次再訪問到他就可以
直接跳到離他最近的右邊的第一個點 同樣可以解決多次訪問同一個點的問題 當然因為博主比較懶 代碼過兩天再補上吧qwq
T1 http://110.64.92.203/contest/30/problem/1001
T2 http://110.64.92.203/contest/30/problem/1002
T3 http://110.64.92.203/contest/30/problem/1003