Codeforces Round #615 (Div. 3) 题解
题意:
给你四个数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; }