[Ozon Tech Challenge 2020][Codeforces 1305A~1305F]

这场迅速签到后就被F题给教育了...挂机了足足有80+min_(:з」∠)_还是要提高下自己的姿势水平

1305A - Kuroni and the Gifts

将两个数组分别排序后输出即可

#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?' ':'
');
      for(int i=1;i<=n;i++)printf("%d%c",b[i],i<n?' ':'
');
      }
}
View Code

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
",k);
    for(int i=1;i<=k;i++)
      {
      sort(f[i]+1,f[i]+m[i]+1);
      printf("%d
",m[i]);
      for(int j=1;j<=m[i];j++)
        printf("%d%c",f[i][j],j<m[i]?' ':'
');
      }
    return 0;
}
View Code

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
"),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
",ans),0;
}
View Code

1305D - Kuroni and the Celebration

此题做法很多

可以发现一棵树度数为1的点至少会有一个,因此可以每次挑出两个度数为1的点(x,y)进行询问,若结果等于其中一个点,则其必定为根。否则删除这两个点并把新出现的度数为1的点加入集合,最坏情况就是恰好询问(lfloor frac{n}{2} floor)次

#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
",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
",res),0;
      s.erase(x),s.erase(y),del(x),del(y);
      }
    return printf("! %d
",*s.begin()),0;
}
View Code

1305E - Kuroni and the Score Distribution

凭直觉可以得出,当(a_i=i)时满足条件的三元组最多,为(sum_{i=1}^n lfloor frac{i-1}{2} floor)。先把这个值求出来,若其(<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=2cdot maxleft { a_i ight })

#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
"),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?' ':'
');
    return 0;
}
View Code

1305F - Kuroni and the Punishment

做这题的时候感受到了明显的智商被压制

随机取(30)个(a_i),分别对(a_i, a_i-1, a_i+1)分解质因数后,把他们的质因子放入集合。之后对集合内的数一一作为(gcd)求操作次数取最小值即可

补充一下证明:可以发现若将2作为(gcd),则答案最多不超过(n),因此需要操作两次的数字个数不会超过(lfloor frac{n}{2} floor)个,由此可以得出至少有一半的数操作次数(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
",ans),0;
}
View Code
原文地址:https://www.cnblogs.com/DeaphetS/p/12406420.html