• Ozon Tech Challenge 2020 (Div.1 + Div.2, Rated, T-shirts + prizes!)


    A. Kuroni and the Gifts

    题意:T(100)组数据,两组数an,bn,各n(100)个,每组数互不相同,找一种匹配方式使得ai+bj互不相同。

    思路:两组数都进行排序,ai和bi匹配得到的就是严格递增的,互不相同。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 void rd(int &x){
     5  x=0;int f=1;char ch=getchar();
     6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
     8 }
     9 void lrd(LL &x){
    10  x=0;int f=1;char ch=getchar();
    11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
    13 }
    14 const int INF=1e9;
    15 const LL LINF=1e18;
    16 const int N=105;
    17 using namespace std;
    18 int T;
    19 int n;
    20 int a[N],b[N];
    21 int main(){
    22 // freopen("in.txt","r",stdin);
    23  rd(T);
    24  while(T--){
    25   rd(n);
    26   for(int i=1;i<=n;i++)rd(a[i]);
    27   for(int i=1;i<=n;i++)rd(b[i]);
    28   sort(a+1,a+n+1);sort(b+1,b+n+1);
    29   for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
    30   for(int i=1;i<=n;i++)printf("%d ",b[i]);puts("");
    31  }
    32  return 0;
    33 }
    34 /**/
    View Code

    B. Kuroni and Simple Strings

    题意:长度为n(1000)的括号串,每次操作可以选择一个子序列(下标不连续),这个子序列的前一半是'(',后一半是')',将其删除。问最少几次操作可以使得再也无法进行下一次操作。

    思路:结论:答案小于等于1。证明:设所有'('的坐标为ai,所有')'的坐标为bi,分别进行排序,我们找到最后一个a[i] < b[cnt_b-i+1]的i,将ai的前缀及对应b的后缀删除,剩下的序列中最左面的'('也在最右面的')'的右面。所以一次操作即可完成,而如果一开始就是不能操作的序列,答案就是0次。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 void rd(int &x){
     5  x=0;int f=1;char ch=getchar();
     6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
     8 }
     9 void lrd(LL &x){
    10  x=0;int f=1;char ch=getchar();
    11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
    13 }
    14 const int INF=1e9;
    15 const LL LINF=1e18;
    16 const int N=1005;
    17 using namespace std;
    18 int n;
    19 char s[N];
    20 int a[N],b[N],cnt1,cnt2;
    21 int main(){
    22 // freopen("in.txt","r",stdin);
    23  scanf("%s",s+1);
    24  n=strlen(s+1);
    25  for(int i=1;i<=n;i++)
    26   if(s[i] == '(')a[++cnt1]=i;
    27   else b[++cnt2]=i;
    28  if(!cnt1 || !cnt2 || a[1] > b[cnt2])printf("0
    ");
    29  else {
    30   int tmp;
    31   for(int i=1;i<=min(cnt1,cnt2);i++)if(a[i] < b[cnt2-i+1])tmp=i;else break;
    32   printf("1
    %d
    ",tmp*2);
    33   for(int i=1;i<=tmp;i++)printf("%d ",a[i]);
    34   for(int i=tmp;i>=1;i--)printf("%d ",b[cnt2-i+1]);
    35   puts("");
    36  }
    37  return 0;
    38 }
    39 /**/
    View Code

    C. Kuroni and Impossible Calculation

    题意:n(2e5)个数字,求两两数之间差的绝对值的乘积对m(1000)取模后的答案。

    思路:如果存在两个数字对m取模后相同,那么最终答案就是0,容斥原理很容易可以发现,如果n > m那么一定是0,n < m暴力即可。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 void rd(int &x){
     5  x=0;int f=1;char ch=getchar();
     6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
     8 }
     9 void lrd(LL &x){
    10  x=0;int f=1;char ch=getchar();
    11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
    13 }
    14 const int INF=1e9;
    15 const LL LINF=1e18;
    16 const int N=2e5+10;
    17 const int M=1e3+10;
    18 using namespace std;
    19 int n,m;
    20 int a[N];
    21 int main(){
    22 // freopen("in.txt","r",stdin);
    23  rd(n);rd(m);
    24  for(int i=1;i<=n;i++)rd(a[i]);
    25  if(n > m)printf("0
    ");
    26  else {
    27   int ans=1;
    28   for(int i=1;i<=n;i++)
    29    for(int j=i+1;j<=n;j++)
    30     ans=abs(a[i]-a[j])%m*ans%m;
    31   printf("%d
    ",ans);
    32  }
    33  return 0;
    34 }
    35 /**/
    View Code

    反思:最开始我考虑将所有数对m取模后的值统计一下数量,然后对这1000个数之间两两组合计算答案,后来发现并不可以,因为二者之差的绝对值对m取模与二者对m取模后之差的绝对值并不一定相同。可能是x和m-x的关系。比如13 15 7。原数之间的计算加入了绝对值,可以认为是大减小,而大的数对m取模后不一定还大,它导致i和j的组合中一部分应该是大减小对m取模,另一部分应该是小减大对m取模,并不能都按大减小。而要处理这个问题就很麻烦,所以这个思路并不好。

    D. Kuroni and the Celebration

    题意:交互题。给一棵n(1000)个节点的树,每次你可以询问两个点的lca,系统将返回lca是什么,需要你在n/2次询问之内找出树的根。

    思路:先证明几个简单的结论,一棵树两个节点以上的树至少有两个叶子(度数为1的点),考虑一个一个点加上去的过程即可发现。如果两个叶子的lca是他们其中的一个,那么lca必是根,不妨设xy的lca是y,那么y必然是x的祖先,而如果y还有父亲,那么它一定不是叶子节点,所以他没有父亲,是根。如果两个叶子的lca不是他们其中的一个,那么他们两个都不可能是根,因为如果任何节点和根的lca都是根本身。所以每次枚举两个叶子节点,求出lca,如果不是二者之一就删除这两个点继续找,如果是就找到了答案,需要注意最后可能还剩下一个点,那么这个点就是根。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 void rd(int &x){
     5  x=0;int f=1;char ch=getchar();
     6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
     8 }
     9 void lrd(LL &x){
    10  x=0;int f=1;char ch=getchar();
    11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
    13 }
    14 const int INF=1e9;
    15 const LL LINF=1e18;
    16 const int N=1005; 
    17 using namespace std;
    18 int n,r;
    19 int in[N];
    20 int fi[N],nxt[N<<1],to[N<<1],tot;
    21 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;}
    22 queue<int>S;
    23 int main(){
    24 // freopen("in.txt","r",stdin);
    25  rd(n);
    26  for(int i=1;i<n;i++){
    27   int x,y;rd(x);rd(y);
    28   link(x,y);link(y,x);
    29   in[x]++;in[y]++;
    30  }
    31  for(int i=1;i<=n;i++)if(in[i] == 1)S.push(i);
    32  bool flg=0;
    33  while(S.size() >= 2){
    34   int u=S.front();S.pop();
    35   int v=S.front();S.pop();
    36   printf("? %d %d
    ",u,v);
    37   fflush(stdout);
    38   int lc;rd(lc);
    39   if(lc == u || lc == v){
    40    printf("! %d
    ",lc);
    41    flg=1;break;
    42   }
    43   for(int i=fi[u];i;i=nxt[i]){
    44    in[to[i]]--;
    45    if(in[to[i]] == 1)S.push(to[i]);
    46   }
    47   for(int i=fi[v];i;i=nxt[i]){
    48    in[to[i]]--;
    49    if(in[to[i]] == 1)S.push(to[i]);
    50   }
    51  }
    52  if(!flg)printf("! %d
    ",S.front());
    53  return 0;
    54 }
    55 /**/
    View Code

    反思:注意一下交互题的写法吧。每次输出给系统询问的时候,需要加上fflush(stdout);然后再从系统读入数据。

    E. Kuroni and the Score Distribution

    题意:构造一个长度为n(5000)的严格单调递增序列,使得存在m(1e9)组ijk满足i<j<k,ai+aj=ak,不存在合法情况输出-1。

    思路:考虑上界,每个k最多由(k-1)/2组ij组合而成,容易发现ai=i就可以达到这个上界。如果m大于这个上界,那么就不存在合法情况。如果m小于等于这个上界,找到小于m的里面最大的k使得1~k满足ai=i时得到的答案数。接下来考虑构造第k+1位,假设还需要构造o组ai+aj=ak的方案。如果用前2o个数字构造,则无法满足严格单增的情况,那么可以取前k个中的后2o个数字来构造出k+1的值,至于k+2到最后的值,只要取一个比较大的数并且两两之差也比较大的数字即可。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 void rd(int &x){
     5  x=0;int f=1;char ch=getchar();
     6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
     8 }
     9 void lrd(LL &x){
    10  x=0;int f=1;char ch=getchar();
    11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
    13 }
    14 const int INF=1e9;
    15 const LL LINF=1e18;
    16 const int N=5e3+10;
    17 using namespace std;
    18 int n,m;
    19 int s[N];
    20 int main(){
    21 /// freopen("in.txt","r",stdin);
    22  rd(n);rd(m);
    23  for(int i=1;i<=n;i++)s[i]=s[i-1]+(i-1)/2;
    24  if(m > s[n])printf("-1
    ");
    25  else {
    26   for(int i=1;i<=n;i++){
    27    if(s[i] <= m)printf("%d ",i);
    28    else {
    29     if(s[i-1] <= m){
    30      int tmp=m-s[i-1];
    31      if(!tmp)printf("%d ",(int)(1e8)+i*(int)(1e4));//这里不强制转换会变成乱码,需要注意,原因未确定 
    32      else printf("%d ",i-1+i-2*tmp);
    33     }
    34     else printf("%d ",(int)(1e8)+i*(int)(1e4));
    35    }
    36   }
    37   puts("");
    38  }
    39  return 0;
    40 }
    41 /**/
    View Code

    反思:1e4这种数字都要强制类型转换,不然会出现乱码,具体原因尚不明确。还是要通过极特殊情况寻找界限。

    F. Kuroni and the Punishment

    题意:n(2e5)个数字,每次操作是对一个数+1或者-1,问最少几次操作可以使得所有数的最大公约数不为1。

    思路:首先要知道最终的公约数一定是一个质数,不然取它的某个质因子一定存在更优解。如果我们知道最大公约数是多少,我们就可以在On的时间内得到最少的操作数,即x%m和m-x%m之间取较小值(需要特判x<m的情况防止将x变为0)。考虑如果这个约数是2,那么最多操作n次,我们得到了一个不严格的上界。那么最终答案对应操作数大于等于2的数字一定不足n/2个,也就是说操作数小于等于1的数字大于n/2个。那么我们任取一个数,就有一半以上的概率取到这种数字,我们对a[u],a[u]+1,a[u]-1进行质因数分解,枚举每个因子即可对答案进行一次更新。这次更新有超过1/2的概率使得答案为最终答案,那么我进行该操作若干次,比如20次,错误的概率就变得极小,也就是可以认为得到了正确的答案。

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define dl double
     4 void rd(int &x){
     5  x=0;int f=1;char ch=getchar();
     6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
     8 }
     9 void lrd(LL &x){
    10  x=0;int f=1;char ch=getchar();
    11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
    13 }
    14 const int INF=1e9;
    15 const LL LINF=1e18;
    16 const int N=2e5+10;
    17 using namespace std;
    18 int n;
    19 LL a[N];
    20 int Rand(int x){
    21  return ((rand()<<10)+rand())%x;
    22 }
    23 int ANS;
    24 void get_ans(LL x){
    25  LL ans=0;
    26  for(int i=1;i<=n;i++)
    27   if(a[i] < x)ans+=x-a[i];
    28   else ans+=min(a[i]%x,x-a[i]%x);
    29  if(ans > n)return ;
    30  ANS=min(ANS,(int)ans);
    31 }
    32 void work(LL x){
    33  int l=sqrt(x);
    34  for(int i=2;i<=l;i++){
    35   if(x % i)continue;
    36   while(x % i == 0)x/=i;
    37   get_ans(i);
    38  }
    39  if(x != 1)get_ans(x);
    40 }
    41 int main(){
    42 // freopen("in.txt","r",stdin);
    43  rd(n);ANS=0x7fffffff;
    44  for(int i=1;i<=n;i++)lrd(a[i]);
    45  for(int i=1;i<=20;i++){
    46   int u=Rand(n)+1;
    47   if(a[u] > 1)work(a[u]);//防止出现0,1 
    48   if(a[u] > 0)work(a[u]+1);
    49   if(a[u] > 2)work(a[u]-1);
    50  }
    51  printf("%d
    ",ANS);
    52  return 0;
    53 }
    54 /*<比较大小不需要强制类型转换而min需要*/
    View Code

    反思:min里面的两个数字类型必须相同,但<两端数字类型不一定相同。这种利用随机化解决问题都有一个条件,即在时间复杂度内可进行的操作数大于其期望操作数,该题期望操作数为2,而时间复杂度允许的操作数在20左右,这就会使得随机化算法有了意义。

  • 相关阅读:
    C/C++指针精髓转载
    彻底搞定c指针系列转载
    vc根据域名获取IP地址 gethostbyname()函数
    try catch finally的执行顺序
    vc2008中mfc字符串转换待续
    C++字符串完全指引(二)转载
    vc随机字符串
    C++字符串完全指引转载
    编写c++程序的优良习惯
    ReportViewer一些技巧
  • 原文地址:https://www.cnblogs.com/hyghb/p/12416692.html
Copyright © 2020-2023  润新知