题解 简单的玄学
考试的时候给这题分配的时间太少了……
糊的暴力因为忽略了左移和加法优先级的问题爆零了
如果\(m \geqslant 2^n\)输出1 1
否则答案即为 \(\frac{\prod\limits_{2^n-m+1}^{2^n}}{2^{nm}}\)
- 连乘取模性质 \(\prod\limits_i^j\%p\) 如果\(p\in [i, j]\)结果一定为0 证明:连乘里都乘\(p\)了再模\(p\)显然为0
所以对于这道题,m的有效范围只有1e6
所以分子其实可以暴力算
但这题还要求约分,并且要求先约分再取模,与先取模再约分不同
所以在暴算分子之前要把分子中所有2约掉
- \(2^n-a\)中2的个数等于a中2的个数
证明:当\(a\nmid 2\)时,个数均为1
当\(a\mid 2\)时,则a可以表示为\(2^k*b, b\nmid 2\)
所以 \(2^n-a=2^n-2^k*b=2^k(2^{n-k}-b)\),括号里的柿子不能被2整除
得证
所以分子里2的个数等于\((m-1)!\)里2的个数
- 求阶乘中某个因子个数:建议看蓝书p138
所以分母就可以表示出来了
然后暴算分子,注意统计每个数中2的个数要转换到\([1, m-1]\)中再统计,否则这个数已经对p取过模了,再统计个数就炸锅了
还有注意去掉分子中的2是不断乘2关于p的逆元即可
这题挺卡细节的……
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define ld long double
#define usd unsigned
#define ull unsigned long long
#define int long long
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
ll n, m;
const ll p=1e6+3, p2=1e6+2;
namespace force{
ll gcd(ll a, ll b) {return !b?a:gcd(b, a%b);}
void solve1() {
ll t=n*(m-1);
ll t2=(1<<t); ll ans=1;
for (int i=1; i<m; ++i) {
ans *= ((1<<n)-i);
}
ans = t2-ans;
ll g=gcd(ans, t2);
ans/=g; t2/=g;
ans%=p; t2%=p;
printf("%lld %lld\n", ans, t2);
exit(0);
}
}
namespace task1{
ll qpow(ll a, ll b) {
ll ans=1;
while (b) {
if (b&1) ans=ans*a%p;
a=a*a%p; b>>=1;
}
return ans;
}
ll calc(ll n) {
ll ans=0;
for (ll i=2; i<=n; i<<=1) ans+=n/i; //, cout<<i<<' '<<ans<<endl;
return ans;
}
void solve() {
ll b=qpow(2, ((n%p2*((m-1)%p2)%p2-calc(m-1))%p2+p2)%p2);
if (m>=p) {printf("%lld %lld\n", b, b); return ;}
ll t=qpow(2, n), a=1;
const ll inv=qpow(2, p-2);
for (int i=1,i2,t2; i<m; ++i) {
//i2=((t-i)%p+p)%p;
t2=t-i; i2=i;
//if (!i2) {a=0; break;}
while (i2==((i2>>1)<<1)) i2>>=1, t2=t2*inv%p;
if (!t2) {a=0; break;}
//while (i2%2==0) i2/=2;
a=a*t2%p;
}
a = ((b-a)%p+p)%p;
printf("%lld %lld\n", a, b);
}
}
signed main()
{
#ifdef DEBUG
freopen("1.in", "r", stdin);
#endif
n=read(); m=read();
task1::solve();
return 0;
}