做完A~C2时间还剩一个小时当时排名好像是1900+ 因为B样例有一组数据一直不对改bug改了好久 好不容易对了之后还wa了一发 看E1过的人多准备跳D直接开E1 才想了一会就显示这场unr。。。
A:
题意:输入带有n个数的序列(可能重复),改变序列的顺序使序列中mex的前缀和最大,输出改变之后的顺序。mex的意思就是找到一个最小的非负整数。例如mex({1,2,3,5})= 4。
思路:为了使mex最终的和最大,肯定优先取小的,例如你先取0,当前最小非负整数就是1,假设序列中最大值为5,那么我们先取5的话,那此时最小负整数就是0。其次,如果有多个数相等,我们只先取一个,例如{0,1,2.2.3},按照前面所说先取小的那把第一个2取了之后如果我们再继续取2,那此时最小负整数还是3,但取了3之后,结果就变4,因为2之前已经出现过了,造成不了影响,所以把多余的重复的数字最后输出即可。
代码:
while(t--) { int n; cin >> n; for(int i=1;i<=n;i++) { cin >> a[i]; } sort(a+1,a+1+n); vector<int>v; a[n+1]=10000; for(int i=1;i<=n;i++) { if(a[i]==a[i+1]) v.push_back(a[i]); else cout << a[i] << " "; } for(int i=v.size()-1;i>=0;i--) { cout << v[i] << " "; } cout << endl;B:
题意:给你一个n个数的序列,把他们尽可能少的分成几段(顺序可以打乱),且满足相邻两数相加之和等于m的倍数或者这一段只有一个数。输出最少段数
思路:对每个数a[i]%m,统计各各余数的个数,将除m后余数为x的和余数m-x的放到一个数组中,然后把多余的单独放到一个数组中即可。
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1e5+5; int a[N],cnt[N]; int main() { int t; cin >> t; while(t--) { int n,m; cin >> n >> m; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) { cin >> a[i]; cnt[a[i]%m]++; } int ans=0,sum=0;; if(cnt[0]) ans++; for(int i=1;i<=m/2;i++) { sum=0; if(cnt[i]&&cnt[i]==cnt[m-i]) sum++; else sum=abs(cnt[i]-cnt[m-i]); ans+=sum; } cout << ans << endl; } return 0; }
C1:
题意:简单版本中k固定为3,输出一个序列满足a1+a2+...+ak=n,LCM(a1,a2,...,ak)<=n/2
思路:看到小于等于n/2,不难想到把n/2即可然后判断一下n的奇偶性,如果n为奇数我们就先分离出一个1,然后剩下的除以2,满足题意。如果n为偶数,因为k=3,要输出三个数,单纯对半分还不行,如果(n/2)%2==1,说明n/2是奇数,这个时候我们需要让对半分的数每个都减一,然后就分离出一个2,两个小于n/2的偶数和一个2,满足题意。如果(n/2)%2==0,先对半分,再对其中一个对半分,他们的最小公倍数就是n/2,和正好也等于n。综上,就三种情况。
代码:
while(t--) { int n,k; cin >> n >> k; if(n%2==1) cout << (n/2) <<" " <<(n/2) <<" " << 1 << endl; else { if((n>>1)%2==1) cout << (n>>1)-1 << " " << (n>>1)-1 << " " << 2 << endl; else cout << (n>>1) << " " << (n>>2) <<" " << (n>>2) << endl; } }C2:
题意:题意跟C1一样,就是k值不再固定
思路:过了C1这一题就不难想了,不难发现1对最后最小公倍数的结果没影响,我们只需输出k-3个1,此时n更新为n-(k-3)*1,k更新为3,所以我们就可以直接套C1推出来的结论。
思路:过了C1这一题就不难想了,不难发现1对最后最小公倍数的结果没影响,我们只需输出k-3个1,此时n更新为n-(k-3)*1,k更新为3,所以我们就可以直接套C1推出来的结论。
代码:
#include <iostream> #include <cstring> #include <algorithm> #include <set> typedef long long ll; using namespace std; const int N = 1e5+5; int a[N],l[N],r[N],cnt[N]; void solve(long long n) { if(n%2==1) cout << (n/2) <<" " <<(n/2) <<" " << 1 << endl; else { if((n>>1)%2==1) cout << (n>>1)-1 << " " << (n>>1)-1 << " " << 2 << endl; else cout << (n>>1) << " " << (n>>2) <<" " << (n>>2) << endl; } } int main() { int t; cin >> t; while(t--) { int n,k; cin >> n >> k; for(int i=1;i<=k-3;i++) cout << 1 << " "; solve(n-k+3); } return 0; }
E1:
题意:给你含n个数的序列,问你最少能分几段使得每一段数的任意两个数相乘不是完全平方数。
思路:因为任意一个数都可以拆成多个质数相乘,要使两个数相乘为平方数,那么相乘的结果的数的每个质因子要都是偶次幂,那么我们可以先把一个数除以它所有质因子的幂数(该幂数小于该质因子的幂数且是偶数)。然后我们标记下商,如果连续端里出现两次,则此处应该与上段分开。
代码:
#include <bits/stdc++.h> using namespace std; const int N = 2e5+5; int a[N]; int main() { int t;cin >> t; unordered_map<int,int>mp; while(t--) { mp.clear(); int n,k; cin >> n >> k; int ans = 1; for(int i=1;i<=n;i++) { cin >> a[i]; int tmp=a[i]; int cnt=1; for(int j=2;j*j<=x;j++){ int cnt=0;while(tmp%j==0){cnt++;tmp/=j;}//算质因子的幂数 for(int k=1;k<=cnt/2*2;k++) a[i]/=j;//除以偶次方 } if(mp[a[i]]) ans++,mp.clear(); mp[a[i]]=1; } cout << ans << endl; } return 0; }优化:上面这个方法时间复杂度还是太大了,跑了接近两秒了,看了别人的代码因为通过分析可以确定只要比较质因子即可,用线性筛先把所有的质数筛一遍,再去让这个数除以质因子的偶次方即可。只需要加个线性筛然后把算质因子的幂数和配注释那上下几行的代码改成如下(这样只跑了100+ms):
for(int j=1;j<=tot&&prm[j]*prm[j]<=a[i];j++)//tot是在线性筛的时候算出来的总质数个数,prm数组用来存质数,prm[j]*prm[j]就保证了每次除的都是质因子的偶次方 while(a[i]%(prm[j]*prm[j])==0) a[i]/=prm[j]*prm[j];