首页 > 其他分享 >[题解] Codeforces Global Round 23 1746 A B C D E1 F 题解

[题解] Codeforces Global Round 23 1746 A B C D E1 F 题解

时间:2022-10-16 14:34:27浏览次数:90  
标签:23 int 题解 rep Global que fi se define

点我看题

求点赞

A. Maxmina

首先序列全0的情况肯定是NO。否则,如果\(k\ge 3\),则在序列中随便找一个1,把他左边和右边分别用第一种操作不断缩,直到序列长度为k为止,最后用一次2操作变成一个1;如果\(k=2\),直接不断用2操作把序列缩成一个元素即可。所以最后的结论就是只要序列中有1就是YES,否则是NO。

时间复杂度\(O(n)\)。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

int t,n,k,a[60];

int main()
{
  cin>>t;
  rep(tn,t)
  {
    cin>>n>>k;
    bool ok=false;
    rep(i,n)
    {
      cin>>a[i];
      ok|=(a[i]==1);
    }
    puts(ok ? "YES":"NO");
  }
  return 0;
}

B. Rebellion

我们把\(a_j+=a_i\)的操作称为对i的操作。我们是不会先\(a_j+=a_i\),然后再对\(a_j\)操作的,因为这样不如直接把\(a_i\)加到\(a_j\)最后加到的那个位置去。所以我们操作的对象只能是初始序列中,没被任何东西加过的元素。发现对于一个0的操作相当于是把他删除。如果还要对一些1操作,那肯定是操作原序列中最靠前的一些1比较好。假设我们对k个1进行操作,这些1应该去优先填补第i+1个1之后的那些0。如果填补不完,则必须对剩下的0操作,把它们删掉。枚举k,用前缀和计算即可。

时间复杂度\(O(n)\)。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

int t,n,a[100010],bk0[100010],nxt[100010];

int main()
{
  cin>>t;
  rep(tn,t)
  {
    cin>>n;
    rep(i,n) scanf("%d",&a[i]);
    int lst=-1,cc=0;
    for(int i=n-1;i>=0;--i)
    {
      if(a[i]==0) ++cc;
      else
      {
        nxt[i]=lst;lst=i;
        bk0[i]=cc;
      }
    }
    if(lst==-1)
    {
      puts("0");
      continue;
    }
    int ans=bk0[lst],c1=0;
    rep(i,n) if(a[i]==1)
    {
      ++c1;
      int c0;
      if(nxt[i]==-1) ans=min(ans,c1);
      else
      {
        ans=min(ans,c1+max(0,bk0[nxt[i]]-c1));
      }
    }
    printf("%d\n",ans);
  }
  return 0;
}

C. Permutation Operations

这种最优化+构造方案的题,日常先猜个结论:可以把逆序对数变成0。其实确实可以:逆序对数是0的充要条件是,每个数前面都没有比他大的数;后缀+1的操作,我们给以n开头的后缀;后缀+2的操作,我们给以n-1开头的后缀\(\cdots\)后缀+n的操作,我们给以1开头的后缀。这样可以保证每个数前面都没有比他大的数。以上"以x开头的后缀"中的x均指序列中的初始值。

时间复杂度\(O(n)\)。


D. Paths on the Tree

首先每条路径的终点一定是叶子,因为多往下延伸一点总能多点贡献。令\(f_{i,j}\)表示以i为根的子树内,一共有j条路径的最大贡献。按照题目中的要求,如果一共有s个儿子,每个儿子都会分到\(\lfloor\frac js\rfloor\)或者\(\lceil\frac js\rceil\)条路径,且取到每种值的儿子数量是固定的,假设有\(cnt\)个儿子取到\(\lceil\frac js\rceil\)条。以记忆化搜索的方式进行dp转移,转移的时候对所有儿子的\(f_{u,\lceil\frac js\rceil}-f_{u,\lfloor\frac js\rfloor}\)排序,取这个值最大的cnt个儿子取\(\lceil\frac js\rceil\)条路径即可。手算一下会发现,对于任意节点i,会被搜索到的\(f_{i,j}\)只有最多2种,也就是j的合法取值只有最多2种。我一开始用map存j居然T了,所以最好先把每个i的可能的j取值先预处理出来。

时间复杂度\(O(nlogn)\)。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

int t,n,a[100010];
vector <pii> v;

int main()
{
  cin>>t;
  rep(tn,t)
  {
    cin>>n;
    v.clear();
    repn(i,n) scanf("%d",&a[i]),v.pb(mpr(a[i],i));
    sort(v.begin(),v.end());reverse(v.begin(),v.end());
    rep(i,v.size()) printf("%d ",v[i].se);puts("");
  }
  return 0;
}

E1. Joking (Easy Version)

你操作数卡那么紧干嘛,哎哟~

我觉得这题挺难的,为啥小学生都会做啊

不过据说后面几题都是偷的某个伊朗比赛(伊朗人偷伊朗题是吧),最近CF怎么净出幺蛾子

为避免混淆,首先明确定义:一个询问的返回结果有两种:YES(Y),NO(N);一个询问的正确性有两种,真(T) 和 假(F)。

令当前n的可能值的集合为s。当\(|s|\ge 4\)时,把s尽量均匀地分成4份,\(s1,s2,s3,s4\)。使用两次询问,分别问\(\{s1 \cup s2\}\)和\(\{s1\cup s3\}\)。得到返回结果后,讨论这两个询问的正确性是TT、TF还是FT。比如当返回结果为YN时,正确性为TT则目标在s2内;正确性为TF则目标在s1内;正确性为FT则目标在s4内,所以应该淘汰s3。其他三种情况推理类似,具体来说:返回结果为YY淘汰s4,为YN淘汰s3,为NY淘汰s2,为NN淘汰s1。所以每2个这样的询问可以让s的大小大约乘\(\frac 34\)。用下面的代码发现最多需要38次才能把s的大小减少到3以内,也就是76次操作。

int n=100000,cc=0;
while(n>3)
{
  n-=n/4;
  cc++;
}
cout<<cc;

如果现在s的大小<3的话,直接猜2次就行了。否则令s中的3个元素为\(1,2,3\),连续问以下四个问题:\(\{1\},\{2\},\{2\},\{1\}\)。我们的目标是淘汰1,2,3中的任意一个数,这样接下来可以猜两次达到目标。先看中间两个询问的返回结果,如果相同,则这两个正确性必须都是T,这就至少淘汰了一个数了;否则中间两个询问正确性必是一真一假,这时再看边上两个询问,如果返回结果相同,则也必须都是T,成功淘汰至少一个数;剩下的情况就是正确性为YNYN或NYNY了(两种对称的),用和上面类似的方法分类讨论一下,发现答案不可能是2,成功淘汰一个。总操作次数似乎最多刚好82次。

我一开始以为easy version是不用可以猜两次的特殊条件的,结果发现连n=2都做不了

时间复杂度\(O(能过)\),反正不超过\(38\cdot n\)。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

int n;
vector <int> v;

vector <int> comb(vector <int> aa,vector <int> bb)
{
  rep(i,bb.size()) aa.pb(bb[i]);
  return aa;
}

int ask(vector <int> aa)
{
  cout<<"? "<<aa.size()<<' ';
  rep(i,aa.size()) cout<<aa[i]<<' ';cout<<endl;cout.flush();
  string s;cin>>s;return s[0]=='Y' ? 1:0;
}

void guess(int x)
{
  cout<<"! "<<x<<endl;cout.flush();
  string s;cin>>s;
  if(s[1]==')') exit(0);
}

int main()
{
  ios::sync_with_stdio(false);
  cin>>n;
  repn(i,n) v.pb(i);
  while(v.size()>3)
  {
    int bas=v.size()/4,mo=bas+1,mocnt=v.size()%4;
    vector <int> vv[4];
    int cur=0;
    rep(i,4)
    {
      if(i<mocnt) rep(j,mo) vv[i].pb(v[cur++]);
      else rep(j,bas) vv[i].pb(v[cur++]);
    }
    int r1=ask(comb(vv[0],vv[1])),r2=ask(comb(vv[0],vv[2]));
    if(r1&&r2) v=comb(vv[0],comb(vv[1],vv[2]));
    else if(r1&& !r2) v=comb(vv[0],comb(vv[1],vv[3]));
    else if(!r1&&r2) v=comb(vv[0],comb(vv[2],vv[3]));
    else v=comb(vv[1],comb(vv[2],vv[3]));
  }
  if(v.size()==3)
  {
    int r1=ask({v[0]}),r2=ask({v[1]}),r3=ask({v[1]}),r4=ask({v[0]});
    if(r2==r3)
    {
      if(r2==1) guess(v[1]);
      else guess(v[0]),guess(v[2]);
    }
    else if(r1==r4)
    {
      if(r1==1) guess(v[0]);
      else guess(v[1]),guess(v[2]);
    }
    else guess(v[0]),guess(v[2]);
  }
  else rep(i,v.size()) guess(v[i]);
  return 0;
}

E2. Joking (Hard Version)

懒得看题解了,不会。


F. Kazaee

很多不同种类的数,判断是否都合法,取模,考虑哈希(提到哈希大家应该都会做了吧)。

先把序列中所有出现过的数都离散化。给每一种值分配一种随机的权值,一个区间的哈希值就是这个区间内所有数的权值之和。这里哈希值不对任何东西取模,也不能自然溢出,要保证分配的权值不太大(比如1e9就差不多)。对于一个合法的区间,容易发现它的哈希值对当前询问中的k取模一定为0;如果这个区间不合法,发现可以认为这个区间的哈希值是随机的,也就是对k取模的值在\([0,k-1]\)中随机,如果哈希值对k取模的值确实不为0,我们就发现了这个区间不合法。但是不合法区间的哈希值也可能刚好与k同余,比如k=2时甚至有\(\frac 12\)的概率发生。那我们干脆对这个序列多做几次哈希\(\to\)筛不合法区间的过程,比如30次,那一个不合法区间没被筛出来的概率就是\(\frac 1{2^{30}}\),所有不合法区间都被筛出来的概率是\((1-\frac1{2^{30}})^q\),这个概率是0.9999这个级别的,如果还wa可能是你脸太黑了

树状数组维护哈希值,总时间复杂度\(O(30\cdot qlogn)\)。

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

mt19937 rnd(114514);
int n,q,a[300010],bad[300010],aa[300010];
LL H[600010];
vector <int> dsc;
vector <pair <pii,pii> > que;

namespace bit
{
  LL dat[300010];
  LL lowbit(LL k){return k&-k;}
  void upd(LL k,LL val)
  {
    while(k<=n)
    {
      dat[k]+=val;
      k+=lowbit(k);
    }
  }
  LL get(LL k)
  {
    LL ret=0;
    while(k>0)
    {
      ret+=dat[k];
      k-=lowbit(k);
    }
    return ret;
  }
}

int main()
{
  cin>>n>>q;
  rep(i,n) scanf("%d",&a[i]),dsc.pb(a[i]);
  int x,y,z;
  rep(i,q)
  {
    scanf("%d",&x);
    if(x==1)
    {
      scanf("%d%d",&x,&y);--x;dsc.pb(y);
      que.pb(mpr(mpr(1,0),mpr(x,y)));
    }
    else
    {
      scanf("%d%d%d",&x,&y,&z);--x;--y;
      que.pb(mpr(mpr(2,x),mpr(y,z)));
    }
  }
  sort(dsc.begin(),dsc.end());dsc.erase(unique(dsc.begin(),dsc.end()),dsc.end());
  rep(i,n) a[i]=lower_bound(dsc.begin(),dsc.end(),a[i])-dsc.begin();
  rep(i,q) if(que[i].fi.fi==1) que[i].se.se=lower_bound(dsc.begin(),dsc.end(),que[i].se.se)-dsc.begin();
  rep(ti,30)
  {
    rep(i,dsc.size()) H[i]=rnd();
    rep(i,n+3) bit::dat[i]=0;
    rep(i,n) aa[i]=a[i],bit::upd(i+1,H[a[i]]);
    rep(i,q)
    {
      if(que[i].fi.fi==1)
      {
        bit::upd(que[i].se.fi+1,-H[aa[que[i].se.fi]]);
        aa[que[i].se.fi]=que[i].se.se;
        bit::upd(que[i].se.fi+1,H[aa[que[i].se.fi]]);
      }
      else if(bad[i]==0)
      {
        LL val=bit::get(que[i].se.fi+1)-bit::get(que[i].fi.se);
        if(val%que[i].se.se!=0) bad[i]=1;
      }
    }
  }
  rep(i,q) if(que[i].fi.fi==2) puts(bad[i] ? "NO":"YES"); 
  return 0;
}

标签:23,int,题解,rep,Global,que,fi,se,define
From: https://www.cnblogs.com/legendstane/p/16796155.html

相关文章

  • 123按位取反是多少?原码、反码、补码及其运算
    如题,在整数运算中总是不清楚某个数的取反和反码到底有什么区别,遂写下此博客,有参考的地方在文末中会贴出出处。在阅读本文章之后会对你了解计算机中一些基础有所帮助,文章包......
  • CF1153F 题解
    传送门题意有一段长为\(l\)的线段,有\(n\)个区间,左右端点在\([0,l)\)间均匀随机(可能不是整数)。求期望被至少\(k\)段区间覆盖的长度,对\(998244353\)取膜模。题......
  • Codeforces Global Round 23
    A.Maxmina显然结果全为0时,结果为NO,若有1,我们通过操作1使长度变为k,里面包含至少1,通过操作2,结果即为YES1#include<bits/stdc++.h>2usingnamespacestd;3consti......
  • Codeforces Global Round 23题解
    T1link大水题,不想说最后一定可以把一个序列消成长度为\(k\)的带一序列,前提是其原来就有一所以贪心就是如果有一,就行,反之不行codeT2linkwssb,考试的时候居然想了大......
  • Codeforces Global Round 23 (A-E1)个人题解
    A-Maxmina给定一个01串,我们可以将k个数变为他们的最大值(k个数变成1个数),或者将相邻的两个数变为他们的最小值(2个数变成1个数),询问是否可以将这个01串变成仅含有一个1的......
  • 2022-2023-1 20221408《计算机基础与程序设计》第七周学习总结
    第七周学习总结作业信息这个作业属于哪个课程:https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP这个作业的要求在哪里:https://www.cnblogs.com/rocedu/p/9577842......
  • 2022-2023-1 20221415 《计算机基础与程序设计》第七周学习总结
    2022-2023-120221415《计算机基础与程序设计》第六周学习总结作业信息这个作业属于哪个课程<班级的链接>(2022-2023-1-计算机基础与程序设计)这个作业要求在哪......
  • Adobe photoshop 2023更新,最新PS2023中文版新增功能(附PS2023下载)
    ps作为一款电脑必备修图工具,收到广大网友的推崇,目前,该版本已经更新到ps2023版!最新的ps2023帮助你组合、修饰和重新混合您的照片,为您的旧黑白添加新颜色,或者让不需要的东......
  • 2022-2023-1 20221313《计算机基础与程序设计》第七周学习总结
    2022-2023-120221313《计算机基础与程序设计》第七周学习总结作业信息这个作业属于哪个课程<班级的链接>https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP......
  • Codeforces Global Round 23 题解
    ContestLink我是智障。A.MaxminaProblemLink显然当数组中全是\(0\)的时候,最后不可能变成\(1\),因为我们只有相邻取\(\min\)和区间取\(\max\)两种操作,并没有任......