Educational Codeforces Round 120 (Rated for Div. 2) vp补题

文章目录

  • [C 贪心 策略](#C 贪心 策略)
  • [D 组合数学 容斥原理](#D 组合数学 容斥原理)
  • [E 状压 绝对值 贪心](#E 状压 绝对值 贪心)

参考@Ander 的题解

C 贪心 策略

基本策略:操作1改小的,让大的数进行操作2变成小的

cpp 复制代码
void solve(){
    int n,k;cin>>n>>k;
    vector<int>a(n+1),pre(n+1,0);
    int sm=0;
    forr(i,1,n)cin>>a[i];
    sort(a.begin()+1,a.end());
    
    forr(i,1,n){
        pre[i]=pre[i-1]+a[i];
    }

    if(pre[n]+a[1]<=k)cout<<0<<endl;
    else{
        int mn=inf;
        reforr(i,1,n){
            int aftsm=k-pre[i]+a[1];// 修改的部分最后得到的和 
            int aim;
            // aim*(n-i+1)<=aftsm
           	if(aftsm>=0)aim=aftsm/(n-i+1);// aim是a[1]要改成的数
            else aim=(aftsm-n+i)/(n-i+1);// 负数向下取整 
            mn=min(mn,max(a[1]-aim,0ll)+n-i);
        }
        cout<<mn<<endl;
    }
    
}

D 组合数学 容斥原理

cpp 复制代码
const int N = 1e5,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e9+10 ;
int fac[N+10],ifac[N+10];
int qpow(int a,int b,int p){
    int res=1;
    while (b)
    {
        if(b&1)(res*=a)%=p;
        b>>=1;
        (a*=a)%=p;
    }
    return res%p;
}
int inv(int x){
    return qpow(x,mod-2,mod)%mod;
}
void init(){
    fac[0]=1;
    forr(i,1,N){
        fac[i]=fac[i-1]*i%mod;
    }
    ifac[N]=inv(fac[N]);
    reforr(i,0,N-1){
        ifac[i]=ifac[i+1]*(i+1)%mod;
    }
}
int C(int n,int m){
    return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
void solve(){
    int n,k;
    string s;cin>>n>>k>>s;
    s=' '+s;
    /*
        双指针 拓展最长的有k个1的段
        任意取的最长段 1的数量都不变
    */
    vector<int>pre(n+1,0);
    forr(i,1,n){
        pre[i]=pre[i-1]+(s[i]-'0');
    }
    if(pre[n]<k||k==0)return cout<<1<<endl,void();// 不操作
    // 该题 问的是最多进行一次操作 不操作也可以得到1
    // ICPC南昌邀请赛I 问的是进行一次操作
    int ans=0;
    if(k==1){
        vector<int>cnt;
        int z=0;
        forr(i,1,n){
            if(s[i]=='1'){
                cnt.push_back(z);
                z=0;
                if(cnt.size()>1){
                    int tp=cnt.size();
                    ans+=cnt[tp-1]+cnt[tp-2];
                }
            }else z++;
        }
        cnt.push_back(z);
        int tp=cnt.size();
        ans+=cnt[tp-1]+cnt[tp-2]+1;// 进行一次操作也可以不修改
    }else{
        int nowl=1,nowr=0,lstr=-1;
        while (nowr<n)
        {
            while(nowr<n&&pre[nowr+1]-pre[nowl-1]<=k)nowr++;
            if(pre[nowr]-pre[nowl-1]<k)break;// 判断区间1的个数
            int lap=0;// 和上一段重叠部分
            if(lstr>=nowl)lap=C(lstr-nowl+1,pre[lstr]-pre[nowl-1]);// nowl~lstr这一段的1数量不变 就是两段都会算的部分
            (ans+=C(nowr-nowl+1,pre[nowr]-pre[nowl-1])-lap+mod)%=mod;
            while(nowl<=nowr&&pre[nowr]-pre[nowl-1]>=k)nowl++;// 摆脱1数量=k的这一段
            lstr=nowr;
        }
    }
    cout<<ans<<endl;
}

E 状压 绝对值 贪心

题意: r [ i ] = ∑ j = 1 m p [ j ] s [ i ] [ j ] r[i] = \sum_{j = 1} ^m p[j] s[i][j] r[i]=∑j=1mp[j]s[i][j]。给定 x[i] ,问 ∑ i = 1 n ∣ r [ i ] − x [ i ] ∣ \sum_{i = 1}^n |r[i] - x[i]| ∑i=1n∣r[i]−x[i]∣ 的最大值

无论实际情况如何 ,总有: ∣ r i − x i ∣ = sign i ⋅ ( r i − x i ) |r_i - x_i| = \text{sign}_i \cdot (r_i - x_i) ∣ri−xi∣=signi⋅(ri−xi)其中 sign i \text{sign}_i signi 的取值取决于 r i r_i ri 和 x i x_i xi 的大小关系。

由于 n ≤ 10 n \leq 10 n≤10,我们可以枚举所有 2 n 2^n 2n 种符号组合 ( sign 1 , sign 2 , ... , sign n ) (\text{sign}_1, \text{sign}_2, \ldots, \text{sign}_n) (sign1,sign2,...,signn)。

对于固定的符号组合 ,目标函数变为:
∑ i = 1 n sign i ( r i − x i ) = ∑ i = 1 n sign i r i − ∑ i = 1 n sign i x i \sum_{i=1}^n \text{sign}i(r_i - x_i) = \sum{i=1}^n \text{sign}i r_i - \sum{i=1}^n \text{sign}_i x_i i=1∑nsigni(ri−xi)=i=1∑nsigniri−i=1∑nsignixi

这里的关键是:我们不是在猜测实际的符号,而是在尝试所有可能的符号分配方式 。并且符合 r i r_i ri 和 x i x_i xi 的大小关系的符号一定能取到最大值。

因此可以把绝对值符号去掉。

对于每一种符号组合,我们计算:

  • 固定值: − ∑ i = 1 n sign i x i -\sum_{i=1}^n \text{sign}_i x_i −∑i=1nsignixi(这部分与 p p p 无关)
  • 可变值: ∑ i = 1 n sign i r i = ∑ j = 1 m p j ( ∑ i = 1 n sign i s i , j ) \sum_{i=1}^n \text{sign}i r_i = \sum{j=1}^m p_j \left(\sum_{i=1}^n \text{sign}i s{i,j}\right) ∑i=1nsigniri=∑j=1mpj(∑i=1nsignisi,j)

然后我们通过排序找到使可变值最大的排列 p p p。

cpp 复制代码
void solve(){
   int n,m;cin>>n>>m;
   vector<int>x(n+1);vector<string>s(n+1);
   forr(i,1,n)cin>>x[i];
   forr(i,1,n)cin>>s[i];
   int mx=-1;
   vector<int>ans; 
   /*
   因为枚举正负号,绝对值的情况被包含在内,并且肯定是最大值
   如果枚举的正负号不贴合去掉绝对值的情况,必然得不到最大值
   */
   forr(b,0,(1<<n)-1){// 状压
   // 枚举正负符号情况 1:r_i>x_i |r_i-x_i|=r_i-x_i 0:r_i<x_i |r_i-x_i|=-r_i+x_i
      vector<int>tp(m+1);
      vector<pii>v(m+1);// 每个位置的系数 需要记录下标
      forr(j,1,m)v[j]={0,j};
      int xsm=0;
      forr(i,1,n){
         if(b>>(i-1)&1){
            xsm-=x[i];
            forr(j,1,m){
               if(s[i][j-1]-'0')v[j].fir++;
            }
         }else{
            xsm+=x[i];
            forr(j,1,m){
               if(s[i][j-1]-'0')v[j].fir--;
            }
         }
      }
      // cout<<"xsm"<<xsm<<endl;
      // 因为是排列 待填p_i是固定的 系数大的分配大的p_i
      sort(v.begin()+1,v.end());
      int rsm=0;
      forr(j,1,m){
         tp[v[j].sec]=j;
         rsm+=j*v[j].fir;
      }
      // cout<<"rsm"<<rsm<<endl;
      if(rsm+xsm>mx){
         mx=rsm+xsm;
         ans=tp;
      }
   }
   forr(i,1,m)cout<<ans[i]<<' ';cout<<endl;
}  
相关推荐
贾斯汀玛尔斯2 小时前
每天学一个算法--图算法(Graph Algorithms)
数据结构·算法
埃伊蟹黄面2 小时前
C++ —— 智能指针
开发语言·c++·算法
董董灿是个攻城狮2 小时前
马斯克在用炸火箭的方式训练 AGI。。。
算法
Pentane.2 小时前
【力扣hot100】【Leetcode 54】螺旋矩阵|边界控制 算法笔记及打卡(19/100)
算法·leetcode·矩阵
黎阳之光2 小时前
黎阳之光:港口智能体集群,重塑智慧港口新范式
大数据·人工智能·算法·安全·数字孪生
大写的z先生2 小时前
【深度学习 | 论文精读】
深度学习·算法·语言模型
大肥羊学校懒羊羊2 小时前
质因数个数问题:高效分解算法详解
开发语言·c++·算法
米粒12 小时前
力扣算法刷题Day 49(接雨水)
算法·leetcode·职场和发展
探物 AI2 小时前
【感知实战·数据增强篇】深度解析目标检测中的图片数据增强算法,多图演示效果
人工智能·算法·目标检测