[Ozon Tech Challenge 2020][Codeforces 1305A~1305F]
这场迅速签到后就被F题给教育了...挂机了足足有80+min_(:з」∠)_还是要提高下自己的姿势水平
将两个数组分别排序后输出即可
#include<bits/stdc++.h> using namespace std; #define N 110 int T,n,a[N],b[N]; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]); sort(a+1,a+n+1); sort(b+1,b+n+1); for(int i=1;i<=n;i++)printf("%d%c",a[i],i<n?' ':'\n'); for(int i=1;i<=n;i++)printf("%d%c",b[i],i<n?' ':'\n'); } }
1305B - Kuroni and Simple Strings
这题开场还卡了一下...做完D才想出来_(:з」∠)_
左右两边扫一下,把左边的'('和右边的')'一个个去掉直到两个指针相交为止即可
可以证明最多只用操作一次,比赛时没想太多就直接set莽了
#include<bits/stdc++.h> using namespace std; #define N 1010 int n,k,m[N],f[N][N]; set<int>a,b; string s; int main() { cin>>s; n=s.size(); for(int i=0;i<n;i++) if(s[i]=='(')a.insert(i+1); else b.insert(i+1); while(a.size() && b.size()) { auto l=a.begin(); auto r=b.rbegin(); if((*l)>(*r))break; k++; while(l!=a.end() && r!=b.rend() && (*l)<(*r)) f[k][++m[k]]=*l,f[k][++m[k]]=*r,l++,r++; for(int i=1;i<=m[k];i++) if(a.count(f[k][i]))a.erase(f[k][i]); else b.erase(f[k][i]); } printf("%d\n",k); for(int i=1;i<=k;i++) { sort(f[i]+1,f[i]+m[i]+1); printf("%d\n",m[i]); for(int j=1;j<=m[i];j++) printf("%d%c",f[i][j],j<m[i]?' ':'\n'); } return 0; }
1305C - Kuroni and Impossible Calculation
由抽屉原理可知,当\(n>m\)时,必定存在两个\(a_i\)模\(m\)同余,此时答案为\(0\)
否则可以直接\(O(n^2)\)暴力
#include<bits/stdc++.h> using namespace std; #define N 200001 #define LL long long LL n,m,ans=1,a[N]; int main() { scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++)scanf("%lld",&a[i]); if(n>m)return printf("0\n"),0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) ans=ans*abs(a[i]-a[j])%m; return printf("%lld\n",ans),0; }
1305D - Kuroni and the Celebration
此题做法很多
可以发现一棵树度数为1的点至少会有一个,因此可以每次挑出两个度数为1的点\(x,y\)进行询问,若结果等于其中一个点,则其必定为根。否则删除这两个点并把新出现的度数为1的点加入集合,最坏情况就是恰好询问\(\lfloor \frac{n}{2} \rfloor\)次
#include<bits/stdc++.h> using namespace std; #define N 1001 int n,u,v; set<int>d[N],s,t; int ask(int x,int y) { int res=0; printf("? %d %d\n",x,y); fflush(stdout); scanf("%d",&res); return res; } void del(int x) { for(auto y:d[x]) { d[y].erase(x); if(d[y].size()==1) t.insert(y); } d[x].clear(); } int main() { scanf("%d",&n); for(int i=2;i<=n;i++) { scanf("%d%d",&u,&v); d[u].insert(v); d[v].insert(u); } for(int i=1;i<=n;i++) { s.insert(i); if(d[i].size()==1)t.insert(i); } while(s.size()>1) { int x=*t.begin(); t.erase(x); int y=*t.begin(); t.erase(y); int res=ask(x,y); if(res==x || res==y) return printf("! %d\n",res),0; s.erase(x),s.erase(y),del(x),del(y); } return printf("! %d\n",*s.begin()),0; }
1305E - Kuroni and the Score Distribution
凭直觉可以得出,当\(a_i=i\)时满足条件的三元组最多,为\(\sum_{i=1}^n \lfloor \frac{i-1}{2} \rfloor\)。先把这个值求出来,若其\(<m\)则一定无解
每次赋值时判断当前的组数是否会溢出,若不会溢出将\(m\)减去当前的贡献。若当前把\(a_i\)赋值为\(i\)后会出现溢出的情况,把它赋值成\(a_{i-1}+a_{i-2m}\)即可,这时\(a_i\)就等于\(a_{i-k}+a_{i-2m+k-1}\),恰好有\(m\)对这样的组合。当\(m=0\)时,可以直接将后面的几个数赋值成\(1e9, 1e9-x, 1e9-2x,...\)这样的形式,注意\(x\)的取值就好了。我是直接取\(x=2\cdot max\left \{ a_i \right \}\)
#include<bits/stdc++.h> using namespace std; #define N 5001 int n,m,s,a[N],mx; int main() { mx=999999999; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)s+=(i-1)/2; if(s<m)return printf("-1\n"),0; for(int i=1;i<=n;i++) { if(m==0) { for(int j=n;j>=i;j--) a[j]=mx,mx-=2*max(1,a[i-1]); break; } if(m<(i-1)/2)a[i]=a[i-1]+a[i-2*m],m=0; else a[i]=i,m-=(i-1)/2; } for(int i=1;i<=n;i++) printf("%d%c",a[i],i<n?' ':'\n'); return 0; }
1305F - Kuroni and the Punishment
做这题的时候感受到了明显的智商被压制
随机取\(30\)个\(a_i\),分别对\(a_i, a_i-1, a_i+1\)分解质因数后,把他们的质因子放入集合。之后对集合内的数一一作为\(gcd\)求操作次数取最小值即可
补充一下证明:可以发现若将2作为\(gcd\),则答案最多不超过\(n\),因此需要操作两次的数字个数不会超过\(\lfloor \frac{n}{2} \rfloor\)个,由此可以得出至少有一半的数操作次数\(\le 1\),于是只要我们找到其中一个这样的数并对该数字及其周围的数的质因子进行判断即可。这样每次随机取一个能有\(\frac{1}{2}\)的正确概率,取30个已经足够保险
#include<bits/stdc++.h> using namespace std; #define N 200001 #define LL long long LL n,a[N],ans=N; set<LL>s; void rua(LL n) { for(LL i=2;i*i<=n;i++) while(n%i==0)s.insert(i),n/=i; if(n>1)s.insert(n); } LL cal(LL x) { LL res=0; for(LL i=1;i<=n;i++) res+=min(x-a[i]%x,a[i]>=x?a[i]%x:x); return res; } int main() { scanf("%lld",&n); for(LL i=1;i<=n;i++) scanf("%lld",&a[i]); srand(time(0)),random_shuffle(a+1,a+n+1); for(LL i=1;i<=30;i++) rua(a[i]-1),rua(a[i]),rua(a[i]+1); for(auto i:s)ans=min(ans,cal(i)); return printf("%lld\n",ans),0; }