题解:P8322 『JROI-4』少女幻葬
Solution
大家好啊,我是安徽队长 Reunite。今天我发明了快速
Reunite 说这是联考的某道题的弱化版(其实一模一样)。他在打模拟赛的时候想到了一个快速
首先,不妨设
设
考虑转移。枚举
显然要考虑莫比乌斯反演,枚举
枚举
我们枚举
注意到
考虑一个叫做
将该操作看为
如果直接做,比暴力还劣。但是考虑
时间复杂度大概是
卡常小技巧:避免 vector 循环,避免数组嵌套。
#include<bits/stdc++.h>
#define ll long long
#define ffor(i,a,b) for(int i=(a);i<=(b);i++)
#define roff(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int MAXN=2000+5,MAXM=5000+5,MAXK=43376+5,MOD=998244353;
int n,k,s[MAXM],l[MAXN],r[MAXN],mu[MAXM],flg[MAXM],id[MAXM*MAXM];
ll dp[2][MAXM*MAXM],f[MAXM],tmp[MAXM];
vector<int> pr;
int zzz[MAXM],frac[MAXK],St[MAXM],Lst[MAXM],len[MAXM],pfr[MAXM*20],pc[MAXM];
void init(int mx) {
mu[1]=1;
ffor(j,1,mx) for(int i=j;i<=mx;i+=j) zzz[i]++;
ffor(i,1,mx) St[i]=Lst[i-1]+1,Lst[i]=St[i]+zzz[i]-1;
ffor(j,1,mx) for(int i=j;i<=mx;i+=j) frac[St[i]+(++len[i])-1]=j;
ffor(i,2,mx) {
if(!flg[i]) pr.push_back(i),mu[i]=-1;
for(auto v:pr) {
if(i*v>mx) break ;
flg[i*v]=1;
if(i%v==0) break ;
mu[i*v]-=mu[i];
}
}
ffor(i,1,mx) for(auto p:pr) if(i%p==0) pfr[i*11+(++pc[i])]=p;
return ;
}
int main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>k;
ffor(i,1,n) cin>>l[i]>>r[i],r[i]=r[i]/k,l[i]=(l[i]+k-1)/k;
// n=2000,k=1;
// ffor(i,1,n) l[i]=1,r[i]=5000;
ffor(i,1,n) if(l[i]>r[i]) return cout<<0,0;
int tot=0;
ffor(i,1,5000) s[i]=s[i-1]+5000/i;
ffor(i,l[1],r[1]) dp[1][i]++;
init(5000);
ffor(i,2,n) {
int st=(i&1),lst=st^1;
ffor(d,1,5000) if(mu[d]) {
int m=5000/d;
ffor(j,1,m) f[j]=0;
ffor(k,1,m) {int o=k*d;for(int j=k,jj=s[o-1]+1;j<=m;j+=k,jj++) f[j]+=dp[lst][jj];}
if(mu[d]<0) ffor(j,1,m) f[j]=-f[j];
for(auto p:pr) {
if(p>m) break ;
int ov=m/p*p;
roff(j,m/p,1) f[j]+=f[ov],ov-=p;
}
ffor(a,1,m) {
for(int j=St[a],id=frac[j];j<=Lst[a];j++,id=frac[j]) tmp[id]=f[id];
int k=a*11;
for(int j=1,p=pfr[k+1];j<=pc[a];j++,p=pfr[k+j]) {int v=a/p;for(int j=St[v],id=frac[j];j<=Lst[v];j++,id=frac[j]) tmp[id]-=tmp[id*p];}
for(int j=St[a],jj=Lst[a],idx=frac[j];j<=Lst[a];j++,idx=frac[j],jj--) dp[st][s[idx*d-1]+frac[jj]]+=tmp[idx];
}
}
ffor(k,1,5000) for(int j=k,jj=s[k-1]+1;j<=5000;j+=k,jj++) {
if(k==1||j<l[i]||j>r[i]) dp[st][jj]=0;
else dp[st][jj]%=MOD;
dp[lst][jj]=0;
}
}
int ans=0;
ffor(k,1,43376) ans=(ans+dp[n&1][k])%MOD;
cout<<(ans%MOD+MOD)%MOD;
return 0;
}
对了,卡常也是 Reunite 教我的。