Codeforces Round 1071 (Div. 3) vp补题

最近忙着考驾照...v20min又被喊去吃饭了...慢慢补题吧

D 构造 位运算

题意:计算前缀与的码重(1的个数)

从二进制角度想,一旦 p i p_i pi一位上出现0,之后这一位都没有贡献了,所以要让 p i p_i pi越晚出现0越好

容易想到把1对齐,固定 n o w now now个位上一直有1 ( n o w = n , n − 1 , . . . , 1 , 0 ) (now=n,n-1,...,1,0) (now=n,n−1,...,1,0),其他位上随便枚举

比如n=5时
11111

01111

00111

10111

00011

01011

10011

11011

00001

...

我是选的低几位的固定为1

注意这样枚举,枚举位要和固定位隔一个0

cpp 复制代码
void solve(){
  int n;cin>>n;
  vector<int>a,vis((1<<n),0);
  int now=n;
  /* 错了
  while(now){
    a.push_back(now);
    vis[now]=1;
    now>>=1;
  }
  forr(i,1,(1<<(n-1))-1){
    if(vis[(i<<1)+1])continue;
    a.push_back((i<<1)+1);
    vis[(i<<1)+1]=1;
  }
  forr(i,0,(1<<(n-1))-1){
    a.push_back((i<<1));
  }*/
  while (now>-1)
  {
    int base=max(0,(1<<now)-1),lst=(1<<max(0ll,(n-now-1)))-1;
    a.push_back(base);
    forr(i,1,lst){
      a.push_back((i<<(now+1))+base);
    }
    now--;
  }
  
  for(auto x:a)cout<<x<<' ';
  cout<<endl;
}

E 分类讨论 构造

感觉是很复杂的分类讨论
参考@znzryb dalao题解

关键思路:本题的关键在于"每个选区至少 p i p_i pi张票"。即使我们满足了所有下界,剩余的票也必须分配到某些选区,且不能改变原有的胜负关系。

思路流程:将每个位置胜负关系固定,并"对半分"让票数使用最少→分类讨论剩下的票数情况,多的适当填充进去,少的抽出

  • 填充:
    • 向胜利(票数占多数)地方填充,多的更多
    • 没有获胜的选区,发现偶数"对半分"有空隙可以填补
    • 剩下的票用对方票抵消,维持胜负关系
  • 抽出:
    • 从失败选区抽出,少的更少。为了维持数目 ≥ p i \geq p_i ≥pi,需要对方票来补充。
    • 抽出完成,剩下的票按填充规则继续填充进去
cpp 复制代码
#define rno cout<<"NO"<<endl
#define ryes cout<<"YES"<<endl
void solve(){
  int n,a,b;cin>>n>>a>>b;
  string s;cin>>s;
  vector<int>cnt(2,0);
  for(auto c:s){
    cnt[c-'0']++;
  }
  vector<int>p(n+1);
  int ra=a,rb=b;//resta restb 最少分配后剩余的a,b
  forr(i,1,n){
    cin>>p[i];
    int ca,cb;
    //这种"对半分"的分配方式 是让多数的那一方用的最少
    if(s[i-1]=='0'){
      ca=p[i]/2+1;
      cb=p[i]-ca;
    }else{
      cb=p[i]/2+1;
      ca=p[i]-cb;
    }
    ra-=ca;
    rb-=cb;
  }

  //填充
    auto putin=[&]()->bool{
    if(ra>=0&&rb>=0){ //ab两边都有剩余
      if(cnt[0]&&cnt[1])return ryes,true; //ab都有多数位置 把剩余的往多数里塞
      else if(cnt[0]){ //只有a多数
        ///如p[i]=2x c1=p[i]/2+1=x+1 c2=p[i]-c1=x-1 c2-c1=2 b有1个的空隙能塞
        int del=0; //统计还能塞的空隙
        forr(i,1,n){
          int ca=p[i]/2+1,cb=p[i]-ca;
          del+=max(0ll,ca-cb-1);
        }
        // rb填满空隙后 剩下的和ra一换一 保持a多数不变
        if(ra>=rb-del)return ryes,true;
      }else if(cnt[1]){ //只有b多数 和上面对称
        int del=0;
        forr(i,1,n){
          int cb=p[i]/2+1,ca=p[i]-cb;
          del+=max(0ll,cb-ca-1);
        }
        if(rb>=ra-del)return ryes,true;
      }
    }
    return false;
  };

  if(ra<0&&rb<0)return rno,void();
  else if(ra>=0&&rb>=0){ //ab两边都有剩下的票 填充
    if(putin())return;
  }else{ //有一边剩下的是负数 就是过量用了 需要抽出
    if(ra>=abs(rb)){ //b被过量用了 并且a能把b补上
      forr(i,1,n){
        if(s[i-1]=='0'){ //b多数的已经是最少了 只能从b少数抽回,b少数的地方更少,并用多数的a补上
          int ca=p[i]/2+1,cb=p[i]-ca;
          int pullout=min({cb,ra,abs(rb)});
          ra-=pullout,rb+=pullout;
        }
      }
      //补完了 此时ra>=0,rb=0 分配剩下多出来的
      if(putin())return;
    }
    if(rb>=abs(ra)){ //b把a补上
      forr(i,1,n){
        if(s[i-1]=='1'){ 
          int cb=p[i]/2+1,ca=p[i]-cb;
          int pullout=min({ca,rb,abs(ra)});
          ra+=pullout,rb-=pullout;
        }
      }
      //补完了
      if(putin())return;
    }
  }
  
  rno;
}

F 二分图 构造




参考@znzryb dalao题解

题好长,读了好长时间:

  • agent先把所给二分图中节点涂上rgb三种颜色
  • 玩家b再根据相邻节点颜色从任意一点走到节点1,b不知道整个图的情况和所在节点的颜色,需要仅通过邻居颜色推导方向

往顶点1的方向走,每次不断缩小与1的距离,核心思路就是把颜色和距离变化绑定

偷个懒,直接引用dalao题解的清晰讲解

结论:二分图的每条边相连的两点,到节点1的距离一定相差1,奇偶性不同。于是需要用两种颜色区分是"靠近"(-1)还是"远离"(+1)

一共3种颜色,自然会想到距离模3染色
d i s % 3 = 0 → r = 1 → g = 2 → b dis\%3 =0\rightarrow r\\ =1\rightarrow g\\=2\rightarrow b dis%3=0→r=1→g=2→b

靠近: ( d i s − 1 ) % 3 (dis-1)\%3 (dis−1)%3是"rgb"顺序中前一位颜色

远离: ( d i s + 1 ) % 3 (dis+1)\%3 (dis+1)%3是"rgb"顺序中后一位颜色
每个顶点的邻居颜色组合,要么是两种(靠近 + 远离),要么是一种(全靠近或全远离),且两种颜色的组合必然 "缺一种颜色"------ 而缺失的颜色,恰好是自身的颜色。

cpp 复制代码
string clr="rgb";
void solve1(){
  int n,m;cin>>n>>m;
  vector<int>g[n+5];
  forr(i,1,m){
    int a,b;cin>>a>>b;
    g[a].push_back(b);
    g[b].push_back(a);
  }
  // dij求各点最短距离
  vector<int>dis(n+1,inf),vis(n+1,0);
  auto dij=[&]()->void{
    dis[1]=0;
    priority_queue<pii>q;
    q.push({0,1});
    while (q.size())
    {
      auto [t,now]=q.top();
      q.pop();
      if(vis[now])continue;
      vis[now]=1;
      for(auto x:g[now]){
        if(dis[x]>dis[now]+1){
          dis[x]=dis[now]+1;
          q.push({-dis[x],x});//大根堆
        }
      }
    }
  };
  dij();

  forr(i,1,n){
    cout<<clr[dis[i]%3];
  }cout<<endl;
}
void solve2(){
  int q;cin>>q;
  while(q--){
    int d;cin>>d;
    string ds;cin>>ds;int ls=ds.size();
    map<char,int>id;
    forr(i,0,ls-1)id[ds[i]]=i+1;
    if(id.size()==1){
      cout<<1<<endl;
    }else{
      forr(i,0,2){
        if(!id[clr[i]]){
          cout<<id[clr[(i+3-1)%3]]<<endl;
        }
      }
    }
  }

}
signed main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int _ = 1;
  string s;cin>>s;
  cin >> _;
  while (_--)
  {
    if(s[0]=='f')solve1();
    else solve2();
  }

  return 0;
}
相关推荐
格林威2 小时前
Baumer相机金属弹簧圈数自动计数:用于来料快速检验的 6 个核心算法,附 OpenCV+Halcon 实战代码!
人工智能·数码相机·opencv·算法·计算机视觉·视觉检测·堡盟相机
json{shen:"jing"}2 小时前
js收官总概述
开发语言·python
froginwe112 小时前
Java 文档注释
开发语言
Zsy_0510032 小时前
【C++】stack、queue、容器适配器
开发语言·c++
一起努力啊~2 小时前
算法刷题--栈和队列
开发语言·算法
万行2 小时前
SQL进阶&索引篇
开发语言·数据库·人工智能·sql
打工的小王2 小时前
java并发编程(六)CountDownLatch和回环屏障CyclicBarrier
java·开发语言
VT.馒头2 小时前
【力扣】2694. 事件发射器
前端·javascript·算法·leetcode·职场和发展·typescript