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 ri = \sum_{j = 1} ^m pj sij ri=∑j=1mpjsij。给定 xi ,问 ∑ i = 1 n ∣ r i − x i ∣ \sum_{i = 1}^n |ri - xi| ∑i=1n∣ri−xi∣ 的最大值

无论实际情况如何 ,总有: ∣ 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;
}  
相关推荐
Navigator_Z4 小时前
LeetCode //C - 1089. Duplicate Zeros
c语言·算法·leetcode
cany10004 小时前
C++ -- 可变参数模板
c++
不会C语言的男孩6 小时前
C++ Primer 第2章:变量和基本类型
开发语言·c++
云泽8087 小时前
C++ 可调用对象通关指南:深度解析 Lambda 表达式、function 包装器与 bind 绑定器
开发语言·c++·算法
wlsh157 小时前
Go 迭代器
算法
Tri_Function7 小时前
简单图论大学习
c++
语戚8 小时前
力扣 3161. 块放置查询:线段树解法(Java 实现)
java·算法·leetcode·面试·线段树·力扣·
lqqjuly8 小时前
C++ 完整知识体系—从基础语法到现代 C++23 的系统性总结
c++·c++23
CS创新实验室8 小时前
从顺序表到动态数组:数据结构的永恒基石与现代语言的优雅封装
数据结构·算法
王老师青少年编程8 小时前
信奥赛C++提高组csp-s之FHQ Treap
c++·csp·平衡树·信奥赛·csp-s·提高组·fhq treap