学习笔记8:abc321的CDEF

文章讨论了AtCoder编程比赛中的几个问题,包括C-321-likeSearcher的暴力搜索优化、D-SetMenu的二分法应用以及E-CompleteBinaryTree和F-#(subsetsum=K)withAddandErase的高效求解策略。通过深度优先搜索、数组操作和动态规划技巧展示了算法的核心思想。
摘要由CSDN通过智能技术生成


SuntoryProgrammingContest2023(AtCoder Beginner Contest 321) - AtCoder

感觉c比d难一些,e比f难一些(

C - 321-like Searcher (atcoder.jp)

分析:纯纯暴力(1~9876543210)肯定不行,我们可以爆搜预处理出所有的合法数字,然后排序,在查询时直接输出

#include <iostream>
#include <cstring>
#include<vector>
#include <algorithm>
using namespace std;
#define int long long
const int N = 1000010;

int a[N];
vector<int>v;
void dfs(int last,int u,int mx){
    if(u==mx+1){
        int sum=0;
        for(int i=u-1,j=1;i>=0;i--,j*=10){
            sum+=a[i]*j;
        }
        v.push_back(sum);
        return;
    }
    for(int i=0;i<last;i++){
        a[u]=i;
        dfs(i,u+1,mx);
    }
}
signed main(){
    int n,x,k;
    int sum=0;
    cin>>k;
    for(int i=0;i<=10;i++){
       dfs(10,0,i); 
    }
    sort(v.begin(),v.end());
    //cout<<v.size()<<endl;
    cout<<v[k]<<endl;
    
}

D - Set Menu (atcoder.jp)

分析:每个ai匹配一个bi,暴力是2e5*2e5,显然不行,我们考虑二分,将b数组排序,求一个b数组的前缀和,然后循环a数组,二分出第一个比p-a[i]大的位置,该位置之前的元素,与a[i]之和都是小于p的所以加上a[i]*sum[r-1]就行,该位置到m之间的元素,与a[i]之和都是大于p的所以加上p*(m-r+1)就行

res(a[i]])=a[i]*(r-1)+sum[r-1]+(m-r+1)*p

最终答案是

ans=\sum res

#include <iostream>
#include <cstring>
#include<vector>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2000010;

int a[N],b[N];
int sum2[N];
signed main(){
    int sum=0;
    int n,m,p;
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++) cin>>b[i];
        
    //sort(a+1,a+1+n);
    sort(b+1,b+1+m);
    for(int i=1;i<=m;i++) sum2[i]=sum2[i-1]+b[i];
    int ans=0;
    for(int i=1;i<=n;i++){
        int l=1,r=m+1;
        while(l<r){
            int mid =l+r>>1;
            if(b[mid]<=p-a[i]){
                l=mid+1;
            }else{
                r=mid;
            }
        }
        int res=sum2[r-1]+(r-1)*a[i];
        int tmp=(m-r+1)*p;
        ans+=res+tmp;
    }
    cout<<ans<<endl;
}

E - Complete Binary Tree (atcoder.jp)

分析:数据量很大,查询次数很多,如何快速地求出答案?

仔细观察我们发现距离和树的深度相关

情况1:节点在查询节点的下方,这时距离就是深度之差

情况2:节点是祖先节点或者是祖先节点的另一侧子节点

如何快速求出个数?

对于情况1我我们只需要 设置两个变量一个mn=now 一个mx=now,每次向下mn*=2,mx=mx*2+1,

直到到达查询节点下面第k层,或是无法继续进行,这时贡献是min(mx,n)-mn+1

对于情况2,我们每次将查询节点的编号/2,得到祖先节点,需要注意,祖先节点向下扩大范围时,会包含住之前已经前一个的祖先节点(或者原查询节点)这时的当前答案,就需要再减去,前一个祖先节点下的k-1次扩大的结果,因为是简单路径(不重复) ,所以不应该包括前一个节点下的k-1次扩大的结果,而且前一个节点的k+1次贡献已经统计,所以直接减去即可

now=res(x/2,k)-res(x,k-1)

#include <iostream>
#include <cstring>
#include<vector>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2000010;

int a[N],b[N];
int n;
int sum=0;
int get(int x,int kk){
    if(kk<0){
        return 0;
    }
    int mn=x,mx=x;
    for(int i=0;i<kk;i++){
        mn<<=1;
        mx=mx<<1|1;
        if(mn>n) return 0;
    }
    return min(mx,n)-mn+1;
}
void solve(){
        int k,x;
    cin>>n>>x>>k;
    if(!k) {
        cout<<1<<endl;
        return ;
    }
    sum=get(x,k);
    while(x/2){
        k--;
        sum+=get(x/2,k)-get(x,k-1);
        x/=2;
    }
    cout<<sum<<endl;
}
signed main(){
    int t=1;
    cin>>t;
    while(t--){
        solve();
    }
}

F - #(subset sum = K) with Add and Erase (atcoder.jp)

非常快乐的dp

正反背包结束了

#include <iostream>
#include <cstring>
#include<vector>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2000010,mod=998244353;

int a[N],b[N];
int n;
int sum=0;
int dp[N];
void solve(){
    int q,k;
    cin>>q>>k;
    dp[0]=1;
    while(q--){
        string op;
        int val;
        cin>>op>>val;
        if(op=="+"){
            for(int i=k;i>=val;i--){
                dp[i]=(dp[i]+dp[i-val])%mod;
            }
        }else{
            for(int i=val;i<=k;i++){
                dp[i]=((dp[i]-dp[i-val])%mod+mod)%mod;
            }
        }
        
        cout<<dp[k]<<endl;
    }
    
}
signed main(){
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值