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;
}  
相关推荐
Yzzz-F6 分钟前
Problem - 2043E - Codeforces EDU173
算法
栈溢出了10 分钟前
GraphSAGE 学习笔记
深度学习·神经网络·算法·机器学习
William_wL_12 分钟前
【C++】priority_queue(优先级队列)的使用和实现
c++
代码中介商12 分钟前
C++ STL入门:vector与字符串流详解
开发语言·c++
fqbqrr13 分钟前
2605C++,C++类的继承1
c++
AI科技星15 分钟前
全域数学版木牛流马(融合仿生兽+古制复原终版优化方案)【乖乖数学】
人工智能·算法·数学建模·数据挖掘·量子计算
王老师青少年编程18 分钟前
csp信奥赛C++高频考点专项训练之字符串 --【字符串排序】:[NOIP 1998 提高组] 拼数
c++·字符串·csp·高频考点·信奥赛·拼数·字符串排序
草莓熊Lotso19 分钟前
【Linux网络】从 0 到 1 实现高性能 UDP 聊天室:深入拆解 Linux 网络编程与线程池架构
linux·运维·服务器·网络·数据库·c++·udp
richard_yuu22 分钟前
数据结构精讲:图的最短路径与关键路径
数据结构·算法
basketball61622 分钟前
C++ iomanip 常用函数
开发语言·c++