題意:
給你四個數a,b,c,d,n.問你是否能將n拆成三個數A,B,C,使得A+a=B+b=C+c。
思路:
先計算三個數的差值的絕對值abs,如果abs大於n則肯定不行,如果小於n,還需判斷(n-abs)%3是否為0,不為0則不行。
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<vector> #include<stack> #include<queue> #include<map> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; ll max(ll a,ll b) {if(a>b) return a;return b;} int main() { int t; scanf("%d",&t); while(t--){ int a[3],n; scanf("%d%d%d%d",&a[0],&a[1],&a[2],&n); sort(a,a+3); int x=(a[2]-a[1])*2+a[1]-a[0]; if(x>n) cout<<"NO"<<endl; else{ if((n-x)%3) cout<<"NO"<<endl; else cout<<"YES"<<endl; } } return 0; }
B - Collecting Packages
題意:
給出直角坐標系中的一些坐標,你只能向上('U')或向右('R')走,問你能否走完這些坐標,如果能請輸出字典序最小的行進路線
思路:
直接按x坐標排序,如果有一個點的縱坐標在上一個點的縱坐標下方,則無法走。輸出順序只需要從上一個點先走R再走U走到下一個點即可
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<vector> #include<stack> #include<queue> #include<map> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; ll max(ll a,ll b) {if(a>b) return a;return b;} const int maxn=1005; struct node{ int x,y; }a[maxn]; int cmp(node a,node b) { if(a.x==b.x) return a.y<b.y; return a.x<b.x; } int main() { int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d%d",&a[i].x,&a[i].y); sort(a,a+n,cmp); int flag=0; for(int i=1;i<n;i++){ if(a[i].y<a[i-1].y){ cout<<"NO"<<endl; flag=1; break; } } if(!flag){ cout<<"YES"<<endl; for(int i=0;i<a[0].x;i++) cout<<"R"; for(int i=0;i<a[0].y;i++) cout<<"U"; for(int i=1;i<n;i++){ for(int j=a[i-1].x;j<a[i].x;j++) cout<<"R"; for(int j=a[i-1].y;j<a[i].y;j++) cout<<"U"; } cout<<endl; } } return 0; }
C - Product of Three Numbers
題意:
給你一個數n,問你是否能將n分解成三個不同的數相乘,可以的話則輸出任一解
思路:
只需要將遍歷從2到
,如果i為n的因子,就將i記錄,並將n除以i。這樣的操作執行三次之后,就可以跳出循環。
如果此時的x不等於1或者循環結束后x不為1,則將x放入數組的尾部,或者將數組的最后一個元素乘上x。
如果最后數組的元素小於3那么則不行,如果等於3的話還需判斷一下,三個因數是否相同。
#include<iostream> #include<algorithm> #include<cstring> #include<vector> using namespace std; vector<int> a; typedef long long ll; int main() { int n,t; scanf("%d",&t); while(t--){ int cnt=0; a.clear(); scanf("%d",&n); for(ll i=2;i*i<=n;i++){ if(n%i==0){ a.push_back(i); cnt++; n/=i; } if(cnt==3) break; } if(n!=1){ if(a.size()==3) a.back()*=n; else a.push_back(n); } if(a.size()<3) cout<<"NO"<<endl; else{ if(a[0]==a[1]||a[1]==a[2]||a[0]==a[2]) cout<<"NO"<<endl; else cout<<"YES"<<endl,cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl; } } return 0; }
D - MEX maximizing
題意:
首先定義MEX為不屬於數組的最小正整數,對於數組[0,0,1,0,2],MEX為3,。對於數組[1,2,3,4]MEX為0.現在給你一個空數組,每次將一個數插入到數組中,插入后可以對數組中的任意數進行任意次+x與-x操作,求每次插入操作后的MEX。
思路:
先將題意轉化:就是求每次插入操作與若干次修改操作后,能覆蓋[0...k]的最長長度。
我們定義a[i] 表示此次詢問時,若干個區間一共有a[i] 個 i 可以填到若干個區間中的 i 位置上(為了滿足題目要求,我們從第一個區間的第i個位置開始填,然后再填第二個區間第i個位置)
然后我們從第一個區間開始檢查。若到當前位置時a[i] != 0,則我們讓a[i] --(表示我們拿一個i填在這個位置上),同時往下一個位置跳,直到遇到一個沒有數可填的位置——a[pos] = 0
#include<iostream> #include<algorithm> using namespace std; const int maxn=4e5+10; int a[maxn]; int main() { int n,x,tem,t=0; scanf("%d%d",&n,&x); while(n--){ int tem; scanf("%d",&tem); a[tem%x]++; while(a[t%x]){ a[t%x]--; t++; } cout<<t<<endl; } return 0; }
E. - Obtain a Permutation
題意:
一個無序的n*m的矩陣,有兩種操作①講某一個位置的數改成任意數②將一整列元素上移一格(最上方元素到最下方),問需要最少多少次操作才能使任意a[i][j]元素變成(n-1)*i+j。
思路:
由於每次操作都只會使一列元素發生變化,所以我們只需要分別計算出每列元素回到正常位置需要的操作數累加即可。
我們首先假設cnt[i]為某列以第i行作為起點,所有元素回到正確位置需要的操作數,並將其初始化為最壞情況i+n。
之后只需要將該列元素遍歷,判斷a[i][j]元素是否在以某個元素為起點的正確位置上,之后將cnt[x]--,然后遍歷得到該列的最小值即可,再將每一列累加即為答案.
#include<iostream> #include<algorithm> #include<cstring> #define inf 0x3f3f3f3f #include<vector> using namespace std; typedef long long ll; const int maxn1=2e4+10; const int maxn=2e5+10; int cnt[maxn],ans=0; vector<int> a[maxn]; int main() { int n,m,x; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&x),a[i].push_back(--x); for(int i=0;i<m;i++){ for(int j=0;j<n;j++) cnt[j]=n+j; for(int j=0;j<n;j++){ if(a[j][i]%m==i&&a[j][i]/m<n){ int tmp=a[j][i]/m; cnt[((j+n-tmp)%n)]--; } } int num=inf; for(int j=0;j<n;j++) num=min(num,cnt[j]); ans+=num; } cout<<ans<<endl; }
F - Three Paths on a Tree
題意:
給一棵樹,找到三個頂點,使三個頂點兩兩之間路徑的並集最大
思路:
必定會有一組最優解,使得 a,b是樹直徑上的端點。
證明:
假設某個答案取連接點x。x最遠的樹到達的點是s,根據樹的直徑算法,s是樹的某個直徑a的端點。假設x的最遠和第二遠的點組成的鏈是b,b就會和a有一段公共部分。我們取a和b相交部分距離s最遠的那個點y。那么取這個鏈上點y的答案一定比x更優
用兩次BFS可以求出直徑的兩個端點,在這個過程中還能順便求出一個端點到樹上每一點的距離。之后再用一次BFS求得另一個端點到樹上每一點的距離。
再枚舉第三個頂點c就可以求出這三個頂點了
最終距離為:[dis(a,b)+dis(b,c)+dis(a,c)]/2
#include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn=2e5+10; vector<int> a[maxn]; int n,vis[maxn],dis1[maxn],dis2[maxn],dis[maxn],pos; void bfs(int x) { memset(vis,0,sizeof(vis)); memset(dis,0,sizeof(dis)); pos=x; vis[x]=1,dis[x]=0; queue<int> q; q.push(x); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0;i<a[u].size();i++){ if(!vis[a[u][i]]){ vis[a[u][i]]=1; dis[a[u][i]]=dis[u]+1; q.push(a[u][i]); if(dis[a[u][i]]>dis[pos]) pos=a[u][i]; } } } } int main() { scanf("%d",&n); int u,v; for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); a[u].push_back(v); a[v].push_back(u); } int a,b,c; bfs(1),a=pos; bfs(pos),b=pos; for(int i=1;i<=n;i++) dis1[i]=dis[i]; bfs(pos); for(int i=1;i<=n;i++) dis2[i]=dis[i]; c=0; for(int i=1;i<=n;i++) if(dis1[i]+dis2[i]>dis1[c]+dis2[c]&&i!=a&&i!=b) c=i; int ans=(dis1[b]+dis1[c]+dis2[c])/2; cout<<ans<<endl<<a<<" "<<b<<" "<<c; return 0; }
