Codeforces Round #857 Div.1/Div.2 CF1801/1802 2A~1F 题解

点我看题(Div2)

Div 2A. Likes

如果要赞最多,肯定是先放所有的点赞,再放所有移除的操作。如果要最少,那就先把赞分成两种:最后被移除的和没被移除的;最后先放所有被移除的,放一个移除一个,然后再把所有没被移除的接在最后即可。

时间复杂度\(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 fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

int t,n,a[110],ans1[110],ans2[110];

int main()
{
  fileio();

  cin>>t;
  rep(tn,t)
  {
    cin>>n;
    int posi=0,nega=0;
    rep(i,n) cin>>a[i];
    rep(i,n) if(a[i]>0) ++posi;else ++nega;
    int bigp=posi,bign=nega,smlp=posi,smln=nega,ansbig=0,anssml=0;
    repn(i,n)
    {
      if(bigp)
      {
        --bigp;
        ++ansbig;
      }
      else
      {
        --bign;
        --ansbig;
      }
      if(i<=nega*2)
      {
        if(i%2==1) ++anssml;
        else --anssml;
      }
      else ++anssml;
      ans1[i]=ansbig;ans2[i]=anssml;
    }
    repn(i,n) cout<<ans1[i]<<' ';cout<<endl;
    repn(i,n) cout<<ans2[i]<<' ';cout<<endl;
  }
  
  termin();
}

Div 2B. Settlement of Guinea Pigs

对每个时刻求出可能需要的最大猪圈数,然后取max即可。对于一个时刻,可以把猪分成两类,已经认证性别的和没认证的。没认证的只能自己住一个猪圈,因为和任意其他猪住一个都可能不合法。已经认证的,如果总数是>0的偶数,肯定是两类都是奇数个的情况最费猪圈;如果是奇数,那就是一类奇数一类偶数,猪圈数是固定的;总数是0那就是0个猪圈。

时间复杂度\(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 fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

int t,n,a[100010];

int calc(int pre)
{
  if(pre==0) return 0;
  if(pre==1) return 1;
  if(pre==2) return 2;
  if(pre%2==0) return (pre-2)/2+2;
  return (pre+1)/2;
}

int main()
{
  fileio();
  
  cin>>t;
  rep(tn,t)
  {
    cin>>n;
    int pre=0,nxt=0,ans=0;
    rep(i,n)
    {
      int x;scanf("%lld",&x);
      if(x==1)
      {
        ++nxt;
        ans=max(ans,nxt+calc(pre));
      }
      else
      {
        pre+=nxt;nxt=0;
        ans=max(ans,calc(pre));
      }
    }
    cout<<ans<<endl;
  }
  
  termin();
}

Div 1A/2C. The Very Beautiful Blanket

怎么每场都有猜谜题呢。。。

首先猜一个:不同数的最大个数是n*m,很多这种题都是先猜能达到最大值的

观察题目中的那个两个田字格异或和相等的限制,其实只要对\(2^0\cdots 2^{63}\)的每个bit都满足就行了。对于每个bit,发现如果我们只把一些行中所有的数都刷上这个bit,其他的行不刷,是满足限制的。比如我们令第1,3,5,6行所有的数都含有\(2^3\),其他的行都不含有,是符合限制的,这时每个田字格的异或和都是0。发现使用这种刷一行或一列的方式,我们可以让矩阵中的所有数都不同。比如我们令数组\(a=\{0,1,2,\cdots 9\},b=\{10,11,12\cdots 19\}\)。矩阵中第i行j列的数含有\(2^{a_k}\)当且仅当i中含有\(2^k\),含有\(2^{b_k}\)当且仅当j中含有\(2^k\)。这么操作其实就是给一些行和一些列都刷上了某一个bit,且成功区别开了所有行和所有列,使得矩阵中每一个数都不同。

时间复杂度\(O(nmlognlogm)\)

点击查看代码
#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 fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

int t,n,m,ans[210][210];
vector <int> r,c;

int main()
{
  fileio();

  rep(i,10) r.pb(1<<i);
  for(int i=10;i<20;++i) c.pb(1<<i);
  cin>>t;
  rep(tn,t)
  {
    cin>>n>>m;
    rep(i,n) rep(j,m)
    {
      ans[i][j]=0;
      rep(k,r.size()) if(i&(1<<k)) ans[i][j]|=r[k];
      rep(k,c.size()) if(j&(1<<k)) ans[i][j]|=c[k];
    }
    cout<<n*m<<endl;
    rep(i,n)
    {
      rep(j,m) printf("%d ",ans[i][j]);
      puts("");
    }
  }
  
  termin();
}

Div 1B/2D. Buying gifts

先把所有department按\(a_i\)从小到大排序,枚举给第一个人买的礼物最大值\(a_i\),此时i右侧的必须全买给第二个人,左侧的可以给第一个人也可以给第二个人。首先令i右侧的\(b_i\)最大值为\(mx\),如果\(mx\ge a_i\),此时的答案就是\(mx-a_i\);否则可以在i左侧的\(b_i\)中找一个与\(a_i\)最接近的再次更新答案,可以用multiset实现。需要特判一下\(i=n\)的情况。

时间复杂度\(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 <LL,LL>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

LL t,n;
vector <pii> a;
multiset <LL> l,r;

LL findNearL(LL x)
{
  auto it=l.lower_bound(x);
  pii ret=mpr(1e18,1e18);
  if(it!=l.end()) ret=min(ret,mpr(llabs(*it-x),*it));
  if(it!=l.begin())
  {
    --it;
    ret=min(ret,mpr(llabs(*it-x),*it));
  }
  return ret.se;
}

int main()
{
  fileio();

  cin>>t;
  rep(tn,t)
  {
    cin>>n;
    a.clear();
    LL x,y;
    rep(i,n)
    {
      scanf("%lld%lld",&x,&y);
      a.pb(mpr(x,y));
    }
    sort(a.begin(),a.end());
    l.clear();r.clear();
    rep(i,a.size()) r.insert(a[i].se);
    LL ans=1e18;
    rep(i,a.size())
    {
      r.erase(r.lower_bound(a[i].se));
      if(i-1>=0) l.insert(a[i-1].se);
      if(i==a.size()-1) break;
      LL mx2=*r.rbegin();
      ans=min(ans,llabs(mx2-a[i].fi));
      if(mx2<a[i].fi&&l.size())
      {
        LL val=findNearL(a[i].fi);
        ans=min(ans,llabs(val-a[i].fi));
      }
    }
    LL val=findNearL(a.back().fi);
    ans=min(ans,llabs(val-a.back().fi));
    cout<<ans<<endl;
  }
  
  termin();
}

Div 1C/2E. Music Festival

首先令\(mx_i\)表示第i个专辑中的最大值,发现如果一个mx小的专辑排在一个mx大的专辑后面(mx相等也一样),那这个专辑是不能产生任何贡献的。所以可以先把所有专辑按\(mx_i\)从小到大排序,我们现在要在其中选一些使得前缀最大值的个数最多。可以用dp实现,\(dp_{i,j}\)表示处理到第i个专辑,此时被选的专辑中mx的最大值为j的最大前缀最大值个数。这状态是\(O(n^2)\)的,但是在处理每个i时改变的状态数都很少,所以可以用线段树维护j这一维,转移时枚举当前专辑中得到的前缀最大值个数,就能得到可以从其转移的j的范围,在线段树上询问即可。注意事项:每个测试点不能直接清空整个线段树,会TLE,要先把值域离散化后清空要用的部分。

时间复杂度\(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 fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

int t,n;
vector <pair <int,vector <int> > > v;
vector <int> dsc;

int getDSC(int x){return lower_bound(dsc.begin(),dsc.end(),x)-dsc.begin();}

namespace st
{
  int n2,dat[800010];
  void upd(int k,int val)
  {
    dat[k]=max(dat[k],val);
    while(k>0)
    {
      k=(k-1)/2;
      dat[k]=max(dat[k],val);
    }
  }
  int qry(int k,int lb,int ub,int tlb,int tub)
  {
    if(ub<tlb||tub<lb) return 0;
    if(tlb<=lb&&ub<=tub) return dat[k];
    return max(qry(k+k+1,lb,(lb+ub)/2,tlb,tub),qry(k+k+2,(lb+ub)/2+1,ub,tlb,tub));
  }
}

int main()
{
  fileio();

  cin>>t;
  rep(tn,t)
  {
    cin>>n;
    v.clear();dsc.clear();
    rep(i,n)
    {
      int x,y,mx=-1e9;vector <int> tmp;
      scanf("%d",&x);
      rep(j,x)
      {
        scanf("%d",&y);
        mx=max(mx,y);tmp.pb(y);dsc.pb(y);
      }
      v.pb(mpr(mx,tmp));
    }
    sort(v.begin(),v.end());
    sort(dsc.begin(),dsc.end());dsc.erase(unique(dsc.begin(),dsc.end()),dsc.end());
    rep(i,v.size()) rep(j,v[i].se.size()) v[i].se[j]=getDSC(v[i].se[j]);
    st::n2=1;while(st::n2<dsc.size()) st::n2*=2;
    rep(i,st::n2*2+3) st::dat[i]=0;
    rep(i,v.size())
    {
      vector <int> premx;
      int mx=-1;
      rep(j,v[i].se.size()) if(v[i].se[j]>mx)
      {
        mx=v[i].se[j];
        premx.pb(mx);
      }
      int opt=-1;
      rep(i,premx.size())
      {
        int lb=(i==0 ? 0:premx[i-1]),ub=premx[i]-1;
        int val=st::qry(0,0,st::n2-1,lb,ub)+premx.size()-i;
        opt=max(opt,val);
      }
      st::upd(premx.back()+st::n2-1,opt);
    }
    cout<<st::dat[0]<<endl;
  }
  
  termin();
}

Div 1D/2F. The way home

无论是cf还是atc,这种用比较复杂的状态跑最短路的题还是经常有的。

先明确我们走的思路:当要坐某趟航班钱不够时,就在我们走到过的报酬最高的城市不断补表演,直到钱够做这次航班为止。

\(dist_{i,j}\)表示当前走到i号点,在这次走到i号点之前经过的\(w\)最大的点的编号为j,此时的最小表演数和最大剩余钱数(先按最小表演数从小到大排序,相同的再按剩余钱数从大到小排序)。我们来证明:如果\(dist_{i,j}\)有两个可能的状态\(\{a,b\}\)\(\{c,d\}\)(ac表示最小表演数,bd表示钱数),且a<c,b<d,此时第一个状态一定优于第二个。用一种类似归纳的思想,我们假设已经对这两个状态转移过来的路径上的所有\(dist_{i,j}\)都证明了其最优状态唯一。由于这两个状态在这次到i之前经过的报酬最高的点都是j,因此它们是从同一个\(dist_{j,*}\)转移过来的,在那之后它们的表演数才变得不同。因此第二个状态的d肯定\(<w_j\),因为我们补表演是补到能坐航班就停了,所以剩下的一定\(<w_j\)。因此如果给第一个状态补\(c-a\)次表演,使两个状态表演次数相同,第一个的钱数一定比第二个多,所以第一个一定更优。

然后就用上面的状态跑一个Dijkstra就行了,时间复杂度\(O(nmlog(n^2))\)(可能?没仔细算)。

点击查看代码
#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 <LL,LL>
#define fi first
#define se second
#define pb push_back
#define mpr make_pair

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nEXECUTION TERMINATED";
  #endif
  exit(0);
}

using namespace std;

LL t,n,m,p,w[810];
vector <pii> g[810];
pii dist[810][810];
priority_queue <pair <pii,pii> > q;


int main()
{
  fileio();

  cin>>t;
  rep(tn,t)
  {
    cin>>n>>m>>p;
    repn(i,n) scanf("%lld",&w[i]);
    rep(i,n+3) g[i].clear();
    LL x,y,z;
    rep(i,m)
    {
      scanf("%lld%lld%lld",&x,&y,&z);
      g[x].pb(mpr(y,z));
    }
    rep(i,n+3) rep(j,n+3) dist[i][j]=mpr(-1e18,-1e18);
    dist[1][0]=mpr(0,p);
    q.push(mpr(mpr(0,p),mpr(1,0)));
    while(!q.empty())
    {
      auto fr=q.top();q.pop();
      pii val=fr.fi;LL pos=fr.se.fi,opt=fr.se.se;
      if(dist[pos][opt]>val) continue;
      if(w[pos]>w[opt]) opt=pos;
      rep(i,g[pos].size())
      {
        pii nval=val;
        if(nval.se<g[pos][i].se)
        {
          LL add=(g[pos][i].se-nval.se+w[opt]-1)/w[opt];
          nval.fi-=add;nval.se+=add*w[opt];
        }
        nval.se-=g[pos][i].se;
        if(dist[g[pos][i].fi][opt]<nval)
        {
          dist[g[pos][i].fi][opt]=nval;
          q.push(mpr(nval,mpr(g[pos][i].fi,opt)));
        }
      }
    }
    pii ans=mpr(-1e18,-1e18);
    repn(i,n) ans=max(ans,dist[n][i]);
    if(ans.fi< -1e17) puts("-1");
    else cout<< -ans.fi<<endl;
  }
  
  termin();
}

由于我用小号打的Div2,所以看不到过了一车的那个1F,1E也不会。后面如果有时间会更新1E和1F的题解

UPD: Div.1 E F题解
link

posted @ 2023-03-09 21:20  LegendStane  阅读(863)  评论(0编辑  收藏  举报