Codeforces Round #615 (Div. 3)
QAQ:
A. Collecting Coins
题意:
有三堆硬币,数目分别是a,b,c,现在你手上还有n枚硬币,问能否通过合理分配n枚硬币到这三堆硬币里,使得这三堆硬币数目相同。
思路:
求所有硬币的和sum,check一下sum是否是三的倍数且每一堆硬币的数目都小于等于sum/3。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { int t; ll a,b,c,n; cin >> t; while(t--) { cin >> a >> b >> c >> n; ll sum = a+b+c+n; if(sum%3!=0) { printf("NO\n");continue; } sum/=3; if(a>sum||b>sum||c>sum) printf("NO\n"); else printf("YES\n"); } return 0; }
B. Collecting Packages
题意:
在一个二维平面上有n个点,一个机器人从原点出发,只能往上或往右走,问机器人能否经过所有的点,如果可以输出最短路径。
思路:
可以将横坐标作为第一关键字,纵坐标作为第二关键字对所有点进行排序,然后暴力模拟即可。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; struct Point { int x,y; Point(int _x=0,int _y =0):x(_x),y(_y){} bool operator <(const Point&b) { if(x==b.x) return y<b.y; return x<b.x; } }; vector<Point>vec; string ans; bool solve(int n) { int x = 0,y = 0; for(int i =0;i<n;i++) { int x1 = vec[i].x,y1 =vec[i].y; if(x1<x) return false; if(y1<y) return false; for(x;x<x1;x++) ans+='R'; for(y;y<y1;++y) ans+='U'; } return true; } int main() { int t; cin >> t; while(t--) { ans = ""; vec.clear(); int n; cin >>n; int x,y; for(int i = 0;i<n;++i) { cin >>x >>y; vec.push_back(Point(x,y)); } sort(vec.begin(),vec.end()); if(solve(n)) { printf("YES\n"); cout << ans << "\n"; } else printf("NO\n"); } return 0; }
C .Product of Three Numbers
题意:
输入一个数n,问能不能拆分成三个大于2且完全不同的数a,b,c,且a*b*c = n;
思路:
可以对n进行素数拆分,然后分类讨论即可。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; struct Prime { int x,n; Prime() {} Prime(int _x,int _n):x(_x),n(_n) {} }; vector<Prime>vec; ll qp(ll x,ll n) { ll res = 1; while(n>0) { if(n&1)res = res*x; x = x*x; n>>=1; } return res; } void solve(ll n) { ll tmp = n; for(int i = 2; i*i<=n; ++i) { int cnt = 0; while(n%i==0) { cnt++; n/=i; } if(cnt>0) vec.push_back(Prime(i,cnt)); } if(n!=1) vec.push_back(Prime(n,1)); // int len = vec.size(); // cout << len << endl; // for(int i = 0;i<len;++i) // { // cout << vec[i].x<< " " << vec[i].n << "\n"; // } ll a=1,b=1,c=1; if(vec.size()>=3) { printf("YES\n"); a = qp(vec[0].x,vec[0].n); b = qp(vec[1].x,vec[1].n); c = tmp/(a*b); cout << a << " " <<b << " " << c << "\n"; return ; } if(vec.size()==2) { if(vec[0].n<2&&vec[1].n<2) { printf("NO\n"); return ; } if(vec[0].n<vec[1].n) swap(vec[0],vec[1]); a = vec[0].x; b = vec[1].x; c = tmp/(a*b); if(c!=a&&c!=b) { printf("YES\n"); cout << a << " " <<b << " " << c << "\n"; return ; } else printf("NO\n"); return ; } if(vec.size()==1) { if(vec[0].n<6) { printf("NO\n"); return ; } a = vec[0].x; b = vec[0].x*vec[0].x; c = tmp/(a*b); printf("YES\n"); cout << a << " " <<b << " " << c << "\n"; return ; } printf("NO\n"); return; } int main() { //freopen("out.txt","w",stdout); int t; cin >> t; while(t--) { vec.clear(); ll n; cin >> n; solve(n); } return 0; }
D. MEX maximizing
题意:
定义一个数组的MEX为数组中第一个未出现的数,开始给你一个空数组以及一个数x,并伴有q次查询,每次查询输入一个数,你可以把这个数加上x无数次或者减x无数次,然后将它插入到数组中,使得每次插入后所得MEX最大。
思路:
首先,(ans-x)%x = ans%x = (ans+x)%x,因此我们就可以用一个数组cnt去模拟上面的数组了,首先对于每一次查询,它的答案不可能比上次查询要小,对于每次输入进来的那个数a,我们可以cnt[a%x]++,这个思路是把原先的一维数轴根据x分为若干段,对于a来说,无论它如何变化,它在某一段中的相对位置不会改变,这个位置也就是a%x,因此,我们可以开始从上一次查询的结果开始,不断循环,查询到底是那一段的那个位置缺失,这个数就是答案。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 4e5+10; int num[maxn]; int main() { int q,x,a; scanf("%d %d",&q,&x); int ans = 0; while(q--) { scanf("%d",&a); num[a%x]++; while(1) { if(num[ans%x]==0) break; num[ans%x]--; ++ans; } cout << ans << "\n"; } return 0; }
E. Obtain a Permutation
题意:
给你一个n*m的矩阵,定义一个move,在一个move你可以进行两种操作,一是选择矩阵中的任何一个元素将他替换成另一个值,二是选择任意一列将他的所有元素循环向上移动一位,问将该矩阵变成
所需要的最小move数。
思路:
首先,通过题意我们可以知道,每一列都是独立的,即对这一列的操作是不会影响到其他列的。因此,我们可以一列一列分开计算。对于某一列的某个元素,我们根据它的位置就可以知道这个位置的值应该是什么,我们枚举这一列的所有元素,假如这个元素是属于这一列的元素,我们可以计算出使它到它应该去的位置所需要的操作数,接下来,枚举操作数,不能移动得到的元素就只能更改了,对于每一个操作数,更新答案即可。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 4e5+10; int num[maxn]; int main() { int q,x,a; scanf("%d %d",&q,&x); int ans = 0; while(q--) { scanf("%d",&a); num[a%x]++; while(1) { if(num[ans%x]==0) break; num[ans%x]--; ++ans; } cout << ans << "\n"; } return 0; }
F. Three Paths on a Tree
题意:
给你一颗有n个结点的树,让你求树上三点a,b,c,使a到b,b到c,c到a,这三条路径所经历的不重复的边数最多。
思路:
首先,这三个点一定有两个端点是在树的直径上,这里通过两次bfs求树的直径,然后枚举c点,因为每个点到两个端点的距离已经通过两次bfs得到,但要注意c不能与a,b点相同。
得到结果除2就是最终答案。
代码:
#include <bits/stdc++.h> 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]+dis2[c]+dis1[c])/2; cout << ans <<endl; cout << a << " "<< b << " " <<c; return 0; }