1. 程式人生 > >[牛客練習賽29D]禁止動規

[牛客練習賽29D]禁止動規

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}\)

意義下的值
注意:

  1. 一個變數可以選多次,也可以不選
  2. 可以走到負半軸

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