A. Petr
题意:
给定一个字符串S,问以stat字符串开头,以end字符串结尾的子字符串有多少种,若完全一样视为一种。
题解:
考虑双哈希,以哈希值来判断字符串相等,可以达到
n
2
n^2
n2复杂度。
wa点:对于end字符串,他的开头下表
j
j
j应该大于等于stat的开头下标
i
i
i并且
j
+
l
e
n
2
−
1
>
=
i
+
l
e
n
1
−
1
j+len2-1>=i+len1-1
j+len2−1>=i+len1−1。
然后赛时一直T68,因为开了个map,在否循环里判断map里是否已经有这个合法字符串,这样就多了一个log的复杂度,会T。
正确做法应该开个vector,最后再sort,unique去重一下。复杂度为
n
2
+
l
o
g
n
n^2+logn
n2+logn
代码写的太丑了,很难debug。下次应该写一个函数。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 200050;
using namespace std;
ll k1[maxn],k2[maxn];
ll p1=131,p2=13331;
ll mod1=1000000007,mod2=1000000009;
ll hash1[maxn],hash2[maxn];
int main(){
string s;
cin>>s;
string s1,s2;
cin>>s1>>s2;
int len1=s1.length(),len2=s2.length();
k1[0]=k2[0]=1;
for(int i=1;i<2022;i++)k1[i]=k1[i-1]*p1%mod1,k2[i]=k2[i-1]*p2%mod2;
for(int i=0;i<s.length();i++){
hash1[i+1]=(hash1[i]*p1%mod1+s[i]-'a'+1)%mod1;
hash2[i+1]=(hash2[i]*p2%mod2+s[i]-'a'+1)%mod2;
}
ll hh11=0,hh12=0,hh21=0,hh22=0;
for(int i=0;i<len1;i++){
hh11=(hh11*p1%mod1+s1[i]-'a'+1)%mod1;
hh12=(hh12*p2%mod2+s1[i]-'a'+1)%mod2;
}
for(int i=0;i<len2;i++){
hh21=(hh21*p1%mod1+s2[i]-'a'+1)%mod1;
hh22=(hh22*p2%mod2+s2[i]-'a'+1)%mod2;
}
int ans=0;
vector<pair<ll,ll> >v;
for(int i=0;i+len1-1<s.length();i++){
for(int j=max(i,i+len1-len2);j+len2-1<s.length();j++){
ll hashl1=(hash1[i+len1]-hash1[i]*k1[len1]%mod1+mod1)%mod1;
ll hashl2=(hash2[i+len1]-hash2[i]*k2[len1]%mod2+mod2)%mod2;
ll hashr1=(hash1[j+len2]-hash1[j]*k1[len2]%mod1+mod1)%mod1;
ll hashr2=(hash2[j+len2]-hash2[j]*k2[len2]%mod2+mod2)%mod2;
ll q1=(hash1[j+len2]-hash1[i]*k1[j+len2-i]%mod1+mod1)%mod1;
ll q2=(hash2[j+len2]-hash2[i]*k2[j+len2-i]%mod2+mod2)%mod2;
if(hashl1==hh11&&hashl2==hh12&&hashr1==hh21&&hashr2==hh22)v.push_back(make_pair(q1,q2));
}
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
cout<<v.size()<<"\n";
return 0;
}
B. DNA Evolution
题意:
你有一个由A,T,G,C组成的字符串S,有两种操作,一种op=1,将x处的字符修改为C,第二种操作,给你一个长度不超过10的字符串e,e循环出现,(例如:eeeee),问
[
l
,
r
]
[l,r]
[l,r]区间内S字符串和(eeee)有几个字符一样。
题解:
考虑维护4种字符以及长度为1-10的全部状态,用多维线段树维护。
tree[id][k][len][m],id表示线段树下标,k表示字符,len表示字符串长度,m表示当前位置取余len后的值。维护每个状态的sum值。
这题线段树开4倍空间会MLE,树状数组会少四倍空间。
树状数组:
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 100005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
int tree[maxn][4][11][10];
int lowbit(int x){return x&(-x);}
void add(int x,int pos,int k,int val){
for(;x<maxn;x+=lowbit(x)){
for(int j=1;j<=10;j++){
tree[x][k][j][pos%j]+=val;
}
}
}
int sum(int i,int xh,int k,int len){
int ans=0;
for(;i;ans+=tree[i][k][len][xh],i-=lowbit(i));
return ans;
}
int query(int l,int r,int xh,int len,int k){
return sum(r,xh,k,len)-sum(l-1,xh,k,len);
}
map<char,int>mp;
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
mp['A']=0;mp['T']=1;mp['G']=2;mp['C']=3;
string s;
cin>>s;
for(int i=0;i<s.length();i++){
add(i+1,i+1,mp[s[i]],1);
}
int q;
cin>>q;
int op,l,r;
string ss;
while(q--){
cin>>op;
if(op==1){
cin>>l>>ss;
add(l,l,mp[s[l-1]],-1);
add(l,l,mp[ss[0]],1);
s[l-1]=ss[0];
}
else{
cin>>l>>r>>ss;
int len=ss.length();
int ans=0;
for(int i=0;i<ss.length();i++){
ans+=query(l,r,(i+l)%len,len,mp[ss[i]]);
}
cout<<ans<<"\n";
}
}
return 0;
}
线段树:
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 100005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
int tree[maxn<<2][4][11][10];
void pushup(int k,int id){
for(int i=1;i<=10;i++){
for(int j=0;j<i;j++){
tree[id][k][i][j]=tree[id<<1][k][i][j]+tree[id<<1|1][k][i][j];
}
}
return ;
}
void update(int id,int l,int r,int k,int pos,int val){
if(l==r){
for(int i=1;i<=10;i++){
tree[id][k][i][pos%i]+=val;
}
return ;
}
int mid=(l+r)>>1;
if(mid>=pos)update(id<<1,l,mid,k,pos,val);
else update(id<<1|1,mid+1,r,k,pos,val);
pushup(k,id);
}
int query(int id,int l,int r,int k,int xh,int lx,int ly,int len){
if(l>=lx&&r<=ly)return tree[id][k][len][xh];
int mid=(l+r)>>1;
int ans=0;
if(mid>=lx)ans+=query(id<<1,l,mid,k,xh,lx,ly,len);
if(mid<ly) ans+=query(id<<1|1,mid+1,r,k,xh,lx,ly,len);
return ans;
}
map<char,int>mp;
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
mp['A']=0;mp['T']=1;mp['G']=2;mp['C']=3;
string s;
cin>>s;
for(int i=0;i<s.length();i++){
update(1,1,s.length(),mp[s[i]],i+1,1);
}
int q;
cin>>q;
int op,l,r;
string ss;
while(q--){
cin>>op;
if(op==1){
cin>>l>>ss;
update(1,1,s.length(),mp[s[l-1]],l,-1);
update(1,1,s.length(),mp[ss[0]],l,1);
s[l-1]=ss[0];
}
else{
cin>>l>>r>>ss;
int len=ss.length();
int ans=0;
for(int i=0;i<ss.length();i++){
// cout<<query(1,1,s.length(),mp[ss[i]],(i+l)%len,l,r,len)<<"\n";
ans+=query(1,1,s.length(),mp[ss[i]],(i+l)%len,l,r,len);
}
cout<<ans<<"\n";
}
}
return 0;
}
D. Find Maximum
题意:
f
(
x
)
=
∑
i
=
0
n
−
1
a
i
b
i
t
(
i
)
,
b
i
t
(
i
)
=
(
x
>
>
i
)
&
1
f(x)=\sum_{i=0}^{n-1}a_ibit(i),bit(i)=(x>>i)\&1
f(x)=∑i=0n−1aibit(i),bit(i)=(x>>i)&1
x属于
[
1
,
n
−
1
]
[1,n-1]
[1,n−1]
题解:
对n二进制处理,若当前位为1,则可以不取当前位,后面的位置全取,也可以就按照n取。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 200050;
using namespace std;
ll a[maxn];
ll sum[maxn];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
string s;
ll ans=0;
cin>>s;
reverse(s.begin(),s.end());
ll now=0;
// cout<<s<<"\n";
for(int i=0;i<n;i++){
// cout<<sum[n-i]<<" "<<now<<"\n";
if(s[i]=='1'){
ans=max(ans,now+sum[n-i-1]);
now+=a[n-i];
}
}
ans=max(ans,now);
cout<<ans;
return 0;
}
E. Bots
题意:
有两个机器人,每个机器人可以走n步,问走完后可能存在多少位置。
题解:
一共可以走
2
n
2n
2n步,答案其实就是
∑
i
=
0
n
∑
j
=
0
n
C
i
+
j
i
\sum_{i=0}^n\sum_{j=0}^nC_{i+j}^i
∑i=0n∑j=0nCi+ji
首先对
∑
j
=
0
n
C
i
+
j
i
\sum_{j=0}^nC_{i+j}^i
∑j=0nCi+ji求解
=
C
i
i
+
C
i
+
1
i
+
.
.
.
+
C
i
+
n
i
=C_i^i+C_{i+1}^i+...+C_{i+n}^i
=Cii+Ci+1i+...+Ci+ni
=
C
i
+
1
i
+
1
+
C
i
+
1
i
+
.
.
.
C
i
+
n
i
=C_{i+1}^{i+1}+C_{i+1}^i+...C_{i+n}^i
=Ci+1i+1+Ci+1i+...Ci+ni
由于
C
n
m
=
C
n
−
1
m
+
C
n
−
1
m
−
1
C_n^m=C_{n-1}^m+C_{n-1}^{m-1}
Cnm=Cn−1m+Cn−1m−1
递推得:
∑
j
=
0
n
C
i
+
j
i
=
C
i
+
n
+
1
i
+
1
=
C
i
+
n
+
1
n
\sum_{j=0}^nC_{i+j}^i=C_{i+n+1}^{i+1}=C_{i+n+1}^n
∑j=0nCi+ji=Ci+n+1i+1=Ci+n+1n
答案转化为
∑
i
=
0
n
C
i
+
n
+
1
n
\sum_{i=0}^nC_{i+n+1}^n
∑i=0nCi+n+1n
=
C
n
+
1
n
+
C
n
+
2
n
+
.
.
.
+
C
2
n
+
1
n
=C_{n+1}^n+C_{n+2}^n+...+C_{2n+1}^n
=Cn+1n+Cn+2n+...+C2n+1n
=
C
n
+
1
n
+
1
−
1
+
C
n
+
1
n
+
C
n
+
2
n
+
.
.
.
+
C
2
n
+
1
n
=C_{n+1}^{n+1}-1+C_{n+1}^n+C_{n+2}^n+...+C_{2n+1}^n
=Cn+1n+1−1+Cn+1n+Cn+2n+...+C2n+1n
=
C
2
n
+
2
n
+
1
−
1
=C_{2n+2}^{n+1}-1
=C2n+2n+1−1
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 3002000
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
ll a[maxn];
ll b[maxn];
ll C(ll n,ll m){
return a[n]*b[m]%mod*b[n-m]%mod;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n;
cin>>n;
a[0]=1;
for(int i=1;i<=2*n+2;i++)a[i]=a[i-1]*i%mod;
b[2*n+2]=q_pow(a[2*n+2],mod-2,mod);
for(int i=2*n+1;i>=0;i--)b[i]=b[i+1]*(i+1ll)%mod;
cout<<(C(2*n+2,n+1)-1ll+mod)%mod;
return 0;
}
F. Purification
题意:
你要清除所有的方块,方块"."可以使用法术,清除这一行以及这一列,方块"E"不能使用。输出次数最少的方案或没有。
题解:
方案最少就是n次,选择n个行或者n个列就行了,2b题,输出要按照行列输出,n个列的情况下输出反了一直wa。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 200050;
using namespace std;
vector<int>g[maxn],h[maxn];
int xh[maxn]={0},xl[maxn]={0};
int main(){
int n;
char s;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>s;
if(s=='.'){
g[i].push_back(j),h[j].push_back(i);
xh[i]=1;xl[j]=1;
}
}
}
int sumh=0,suml=0;
for(int i=1;i<=n;i++){
if(xh[i])sumh++;
if(xl[i])suml++;
}
if(sumh<n&&suml<n){
cout<<"-1\n";
return 0;
}
if(sumh==n){
for(int i=1;i<=n;i++){
cout<<i<<" "<<g[i][0]<<"\n";
}
}
else{
for(int i=1;i<=n;i++){
cout<<h[i][0]<<" "<<i<<"\n";
}
}
return 0;
}
H. Dima and a Bad XOR
题意:
n行,每行m个数,要在n行中每行选一个数,使得XOR不为0.输出任意方案。
题解:
很容易想到二进制拆分,对于每一位,只要能凑够奇数个1就能不为0.有1取1,若为奇数,直接就符合了,若为偶数去找1的能不能选0,就是说选1的行存不存在0,若存在选个0就可以了。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 200050;
using namespace std;
int a[555][555];
int b[555][555];
int ans[555];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int i=0;i<=10;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=m;k++){
b[j][k]=(a[j][k]>>i)&1;
}
}
int sum=0;
int indexi=0,indexj=0;
for(int j=1;j<=n;j++)ans[j]=0;
for(int j=1;j<=n;j++){
int num=0,pos1,pos2;
for(int k=1;k<=m;k++){
num+=b[j][k];
if(b[j][k])ans[j]=k;
if(b[j][k]==0)pos1=j,pos2=k;
}
if(num)sum++;
if(num>=1&&num<m)indexi=pos1,indexj=pos2;
}
if(sum%2){
cout<<"TAK\n";
for(int j=1;j<=n;j++){
if(ans[j]==0)cout<<"1 ";
else cout<<ans[j]<<" ";
}return 0;
}
else if(indexi!=0&&indexj!=0){
cout<<"TAK\n";
for(int j=1;j<=n;j++){
if(ans[j]==0)cout<<"1 ";
else if(j==indexi)cout<<indexj<<" ";
else cout<<ans[j]<<" ";
}return 0;
}
}
cout<<"NIE\n";
return 0;
}
I. RGB Substring (hard version)
题意:
一次可以修改一个字符,问字符串中k个字符符合“RGB”循环的最小修改次数。
题解:
只有3种循环状态,三种都判断一遍就行了。当时2b了,直接上了线段树。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 200050;
using namespace std;
string s;
char mp[3]={'R','G','B'};
struct node{
int ans[3];
int len;
}tree[maxn<<2];
void pushup(int id){
tree[id].len=tree[id<<1].len+tree[id<<1|1].len;
for(int i=0;i<3;i++){
tree[id].ans[i]=tree[id<<1].ans[i]+tree[id<<1|1].ans[(i+tree[id<<1].len)%3];
}
return;
}
void build(int id,int l,int r){
if(l==r){
tree[id].len=1;
for(int i=0;i<3;i++){
tree[id].ans[i]=(s[l]!=mp[i]);
}
return ;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
pushup(id);
}
node query(int id,int l,int r,int lx,int ly){
if(l>=lx&&r<=ly){
return tree[id];
}
int mid=(l+r)>>1;
int minn=0x3f3f3f3f;
node a,b;
int f1=0,f2=0;
if(mid>=lx)a=query(id<<1,l,mid,lx,ly),f1=1;
if(mid<ly)b=query(id<<1|1,mid+1,r,lx,ly),f2=1;
if(f1&&f2){
node c;
c.len=a.len+b.len;
for(int i=0;i<3;i++){
c.ans[i]=a.ans[i]+b.ans[(i+a.len)%3];
}
return c;
}
if(f1)return a;
if(f2)return b;
}
int main(){
int t;
cin>>t;
while(t--){
int n,k;
cin>>n>>k;
cin>>s;
s=" "+s;
build(1,1,n);
int ans=0x3f3f3f3f;
for(int i=1;i+k-1<=n;i++){
node now=query(1,1,n,i,i+k-1);
for(int j=0;j<3;j++){
ans=min(ans,now.ans[j]);
}
}
cout<<ans<<"\n";
}
return 0;
}
J. Up the Strip
题意:
现在你在n的位置,你有两种操作,一种你可以跳到
n
−
x
,
(
x
−
>
[
1
,
n
−
1
]
)
n-x,(x->[1,n-1])
n−x,(x−>[1,n−1])的位置。一种你可以跳到
n
x
,
(
x
−
>
[
2
,
n
]
)
\frac{n}{x},(x->[2,n])
xn,(x−>[2,n]),问跳到1的方案数。
题解:
反向思考,从1跳到n,有两个操作,一个可以跳到
[
i
+
1
,
n
]
的
位
置
[i+1,n]的位置
[i+1,n]的位置
一个对于所有的
j
,
[
i
∗
j
,
i
∗
j
+
j
)
区
间
都
可
以
由
i
跳
到
j,[i*j,i*j+j)区间都可以由i跳到
j,[i∗j,i∗j+j)区间都可以由i跳到。
区间数量为
n
2
+
n
3
+
.
.
.
.
\frac{n}{2}+\frac{n}{3}+....
2n+3n+....是调和级数,时间复杂度为
n
l
o
g
n
nlogn
nlogn
对于区间加法,我们用差分数组可以O(1)维护。
初始值dp[1]=1,由于ans[1]=0,所有dp[2]=-1。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 5002000
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
ll dp[maxn];
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
ll n,m;
cin>>n>>m;
dp[1]=1;dp[2]=m-1;
for(int i=1;i<=n;i++){
dp[i]=(dp[i]+dp[i-1])%m;
dp[i+1]=(dp[i+1]+dp[i])%m;//j=[i+1,n],dp[j]+=dp[i]
for(int j=2;j*i<=n;j++){
dp[j*i]=(dp[j*i]+dp[i])%m;
if(j*i+j<=n)dp[j*i+j]=((dp[j*i+j]-dp[i])%m+m)%m;
}
}
cout<<dp[n];
return 0;
}