[牛客練習賽29D]禁止動規
阿新 • • 發佈:2018-11-09
description
newcoder
你在一個無限長的數軸上,一開始你在原點
本來你只有兩種操作:向左dp,以及向右dp
然而由於禁止dp
於是你只能另尋出路
萬幸的是,dp之神隨機給了你n個變數,既\(x_1,x_2, ... , x_n\),每個變數的值在\([1,m]\)之間,且是整數
每次你可以選擇一個變數\(x_i\),然後向左走\(x_i\)個單位,或者向右走\(x_i\)個單位
問走到原點右側1單位距離的概率是多大?
既隨機給定n個變數後,存在至少一種從數軸上的0點走到1點的方案的概率
設答案為\(w\),那麼你只需要輸出\(w\times m^n\)在模\(2^{64}\)
注意:
- 一個變數可以選多次,也可以不選
- 可以走到負半軸
data range
\[n,m\le 10^{11}\]
solution
終於學會杜教篩.jpg
我們知道合法方案一定存在兩個數\(x_j,x_k\)互質。
考慮容斥減掉\(x_i\)全部為\(k\)的倍數的方案,那麼答案即為
\[\sum_{i=1}^{m}\mu(i)(\lfloor\frac{m}{i}\rfloor)^n\]
數論分塊+杜教篩求\(\mu(i)\)的字首和即可。
時間複雜度為\(O(n^{\frac{2}{3}})\)。
Code
#include<bits/stdc++.h> #include<algorithm> #include<iostream> #include<cstdlib> #include<iomanip> #include<cstring> #include<complex> #include<vector> #include<cstdio> #include<string> #include<bitset> #include<cassert> #include<ctime> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #define F "a" #define mp make_pair #define pb push_back #define fi first #define se second #define RG register using namespace std; typedef unsigned long long ll; typedef pair<int,int> PI; typedef vector<int>VI; //typedef long long ll; typedef long double dd; const dd eps=1e-6; const int mod=1e4; const int N=2e7+10; const dd pi=acos(-1); const int inf=2147483647; const ll INF=1e18+1; const ll P=100000; inline ll read(){ RG ll data=0,w=1;RG char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar(); return data*w; } inline void file(){ srand(time(NULL)+rand()); freopen(F".in","r",stdin); freopen(F".out","w",stdout); } int pri[N],mu[N];bool vis[N]; inline void sieve(){ vis[1]=mu[1]=1; for(RG int i=2;i<N;i++){ if(!vis[i])pri[++pri[0]]=i,mu[i]=-1; for(RG int j=1;j<=pri[0]&&1ll*i*pri[j]<N;j++){ vis[i*pri[j]]=1;mu[i*pri[j]]=mu[i]*mu[pri[j]]; if(i%pri[j]==0){mu[i*pri[j]]=0;break;} } } for(RG int i=2;i<N;i++)mu[i]+=mu[i-1]; } inline ll poww(ll a,ll b){ RG ll ret=1; for(;b;b>>=1,a=a*a) if(b&1)ret=ret*a; return ret; } ll n,m,ans; map<ll,ll>premu; ll getmu(ll n){ if(n<N)return mu[n]; if(premu.find(n)!=premu.end())return premu[n]; ll &res=premu[n];res=1; for(RG ll i=2,j;i<=n;i=j+1)j=n/(n/i),res-=getmu(n/i)*(j-i+1); return res; } int main() { n=read();m=read();sieve(); for(RG ll i=1,j;i<=m;i=j+1) j=m/(m/i),ans+=(getmu(j)-getmu(i-1))*poww(m/i,n); printf("%llu\n",ans);return 0; }