標籤:def 一個 接下來 return fine ++ 存在 turn strong
number題目描述
給定整數 \(m,k\),求出正整數 \(n\) 使得 \(n+1,n+2,…,2n\) 中恰好有 \(m\) 個數在二進位下恰好有 \(k\) 個 \(1\)。 有多組資料。
輸入資料
第一行一個整數 \(t\) 表示資料群組數。接下來 \(t\) 行每行兩個整數 \(m\),\(k\)。
輸出資料
每組資料輸出一行兩個整數,第一個數表示 \(long \ long\) 範圍內任意一個滿足條件的 \(n\),第二個數表示滿足條件的 \(n\) 的個數(無窮多用\(-1\)表示)。 保證 \(10^{18}\) 以記憶體在滿足條件的 \(n\)。
如果每組資料第一個數全部正確,得 \(4\) 分。
如果每組資料第二個數全部正確,得 \(6\) 分。
資料範圍
對於 \(10\%\) 的資料, \(k=2\)。
對於 \(20\%\) 的資料, \(k<=3\)。
對於另外 \(50\%\) 的資料, 保證滿足條件的 \(n\) 均在 \(10^{18}\) 以內。
對於 \(100\%\) 的資料, \(t<=2000\), $0<=m<=10^{18}, \(1<=k<=64\)。
打表吧。
然後發現\(k\)一定時,\(m\)隨\(n\)增大在整數域上連續增大。
進一步發現,其實\(m\)變化時的\(n\)是二進位下\(k-1\)個\(1\)從小到大排序而成的
於是可以預先處理組合數求一下啦
要特判\(m=0\)
Code:
#include <cstdio>#define ll long longll C[70][70];void init(){ C[0][0]=1; for(int i=1;i<=67;i++) { C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; }}int t;ll cal(int k,ll m){ if(m==-1) return 0; ll ans=0; while(k) { int pos=k; while(C[pos][k]<=m) ++pos; --pos; ans|=1ll<<pos; m-=C[pos][k]; k--; } return ans;}int main(){ init(); scanf("%d",&t); while(t--) { ll m;int k; scanf("%lld%d",&m,&k); --k; if(!k) printf("1 -1\n"); else { ll r=cal(k,m),l=cal(k,m-1); printf("%lld %lld\n",l+1ll,r-l); } } return 0;}
2018.10.13
number 解題報告