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[i-1][1]到dp[i-1][j-k]\)可以转移到\(dp[i][j]\)

\(dp[i-1][j+k]到dp[i-1][m]\)同样可以转移到\(dp[i][j]\)

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

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

注意当k==0时,一整个\(dp[i-1][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 @ 2022-05-29 10:58  LiAnG24  阅读(58)  评论(0)    收藏  举报