题解 简单的玄学

传送门

考试的时候给这题分配的时间太少了……
糊的暴力因为忽略了左移和加法优先级的问题爆零了

如果\(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;
}
posted @ 2021-07-13 06:28  Administrator-09  阅读(7)  评论(0编辑  收藏  举报