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();}
}