AtCoder Beginner Contest 253

AtCoder Beginner Contest 253

D - FizzBuzz Sum Hard

题意

找到[1,n]中不是a或b的倍数的数之和

思路

容斥

先算出[1,n]所有数的和

num1表示[1,n]有多少个数是a的倍数

不难发现第一个数是a的一倍,第二个数是a的两倍,依次类推..

最后这些数共有a的(num1(num1+1))/2

下同

num2表示[1,n]有多少个数是b的倍数

num3表示[1,n]有多少个数是lcm(a,b)的倍数

#include<bits/stdc++.h>
#define ll    long long
#define endl '\n'
using namespace std;
const ll inf =0x3f3f3f3f3f;
const ll mod =998244353;
const int N=1e6+7;
void solve(){
    ll n,a,b;cin>>n>>a>>b;
    ll sum=(n*(n+1))/2;
    ll com=(a*b)/__gcd(a,b);
    ll num1=(n/a),num2=(n/b);
    ll num3=(n/com);
    cout<<sum-((num1*(num1+1))/2)*a-((num2*(num2+1))/2)*b+((num3*(num3+1))/2)*com<<endl;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int kase=1;
    // cin>>kase;
    while(kase--){solve();}
}

E - Distance Sequence

题意

给定n m k,问可以构造出多少个序列满足序列长度为n,每个数大于1且小于m,并且相邻两个数的差的绝对值大于等于k(注意k可以等于0)

思路

考虑dp[i][j]表示第i个数为j的合法序列有多少

dp[i1][1]dp[i1][jk]可以转移到dp[i][j]

dp[i1][j+k]dp[i1][m]同样可以转移到dp[i][j]

可以用一个前缀和数组进行优化

当前dp[i][0...m]跑完后需要重新对前缀和进行一个更新

注意当k==0时,一整个dp[i1][0....m]都可以转移

#include<bits/stdc++.h>
#define ll    long long
#define endl '\n'
using namespace std;
const ll inf =0x3f3f3f3f3f;
const ll mod =998244353;
const int N=1e6+7;
ll dp[1010][5050];
ll pre[5050];
void solve(){
    ll n,m,k;cin>>n>>m>>k;
    for(int j=0;j<=m;j++){
        dp[1][j]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=m;j>=0;j--){
        if(k==0){
            dp[i][j]=(dp[i][j]+pre[m])%mod;continue;
        }
        dp[i][j]=(dp[i][j]+((pre[m]-pre[min(m,j+k-1)]+mod)%mod+pre[max(0ll,j-k)]+mod)%mod+mod)%mod;
        }
        for(int j=1;j<=m;j++){
            pre[j]=(pre[j-1]+dp[i][j]+mod)%mod;
        }
    }
    ll ans=0;
    for(int j=1;j<=m;j++){
        ans=(ans+dp[n][j])%mod;
    }
    cout<<ans%mod<<endl;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int kase=1;
    // cin>>kase;
    while(kase--){solve();}
}

F - Operations on a Matrix

题意

给定n*m的矩阵,有三种操作

1 l r x :将[l,r]区间内的列的所有值加上x

2 i x :将第i行的所有值变为x

3 i j:询问第i行第j列的值

思路

考虑到是区间修改,可以联想到树状数组

可以先把所有的操作用一个结构体记录下

记录过程中一同用last[i]=j记录第i行最近被第j个操作改变

vis[i]存的是最近需要访问到第i行的第三个操作的位置

然后遍历所有操作

如果是操作1,那么直接区间修改即可

如果是操作2,我们需要找到后续询问中被该操作影响到的操作3的位置,然后我们对操作3的答案加上a[i].y,并且需要提前减去前面的修改,等到后续遍历到操作3的位置时,再加上修改(也就是最后一个else的内容),这样得到的便是自从当前操作2后,后续操作的贡献。

#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
using namespace std;
const int N=1e6+7;

int n,m,q;
struct A{
    int id,x,y,z;

}a[N];
ll last[N],tr[N],ans[N];
vector<int>vis[N];
int lowbit(int x){return x&(-x);}
ll query(int x){
    ll ans=0;
    while(x){
        ans+=tr[x];
        x-=lowbit(x);
    }
    return ans;
}
void add(int x,int val){
    while(x<=m){
        tr[x]+=val;
        x+=lowbit(x);
    }
}

void solve(){
    cin>>n>>m>>q;
    for(int i=1;i<=q;i++){
        cin>>a[i].id>>a[i].x>>a[i].y;
        if(a[i].id==1){
            cin>>a[i].z;
        }
        if(a[i].id==2){
            last[a[i].x]=i;
        }
        if(a[i].id==3){
            vis[last[a[i].x]].push_back(i);
        }
    }
    for(int i=1;i<=q;i++){
        if(a[i].id==1){
            add(a[i].x,a[i].z);
            add(a[i].y+1,-a[i].z);
        }
        else if(a[i].id==2){
            for(auto x:vis[i]){
                ans[x]=a[i].y-query(a[x].y);
            }
        }
        else {
            ans[i]+=query(a[i].y);
            cout<<ans[i]<<endl;
        }
    }
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T=1;
    // cin>>T;
    while(T--){solve();}
}

posted @   LiAnG24  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
点击右上角即可分享
微信分享提示