BZOJ 3329 Xorequ (数位dp + 矩阵快速幂)

题意:

   给定正整数 n n n, 现有如下方程 x ⊕ 3 x = 2 x x\oplus3x=2x x3x=2x
  任务如下:

  • 求出小于等于 n n n的正整数中,有多少个是该方程的解
  • 求出小于等于 2 n 2^{n} 2n的正整数中,有多少个是该方程的解,模 1 0 9 + 7 10^9+7 109+7
解题思路:

问题一:

  • 将原式子变换 x ⊕ 2 x = 3 x = x + 2 x x\oplus2x=3x=x+2x x2x=3x=x+2x
  • x x x x o r xor xor y y y ≤ \le x x x o r or or y y y ≤ \le x + y x + y x+y
  • 因此 x x x o r or or y y y = = = x + y x + y x+y,所以 x x x a n d and and 2 x = 0 2x=0 2x=0即x转换为2进制后没有相邻的1,数位dp即可
ll dfs(int pos, int sta, bool limit) {
	if (pos == -1) return 1;
	if (!limit && dp[pos][sta] != -1) return dp[pos][sta];
	int up = limit ? digit[pos] : 1;
	ll sum = 0;
	for (int i = 0; i <= up; i++) {
		if (sta && i == 1) continue;
		sum += dfs(pos - 1, i == 1, limit && i == digit[pos]);
	}
	if (!limit) dp[pos][sta] = sum;
	return sum;
}
ll solve(ll x) {
	int cnt = 0;
	while (x) {
		digit[cnt++] = x % 2, x /= 2;
	}
	ll ans = dfs(cnt - 1, 0, true);
	return ans;
}

问题二:

  • 询问 [ 1 , 2 n ] [1,2^n] [12n]有多少个不连续的两个1
  • 显然斐波那契数列(看题解显然…)
  • 比如说 f ( n ) f(n) f(n),第n位取1,那么第n-1位只能取0,所以要加上 f ( n − 2 ) f(n-2) f(n2),第n位取0,那么第n-1位随便取,所以要加上 f ( n − 1 ) f(n-1) f(n1)
  • f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2)
  • 因为n很大,所以要矩阵加速,具体矩阵加速详情可看**luogu P1962**
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Mod = 1e9 + 7;
const int maxn = 1e2;
ll n, dp[maxn][2]; // dp[i][0]代表i位选0的总方案数,。。。
int T, digit[maxn];
struct Matrix {
	ll a[4][4];
	Matrix operator * (const Matrix &b) const {
		Matrix re;
		memset(re.a, 0, sizeof(re.a));
		for (int i = 1; i <= 2; i++)
			for (int k = 1; k <= 2; k++)
				for (int j = 1; j <= 2; j++)
					re.a[i][j] = (re.a[i][j] + a[i][k] * b.a[k][j]) % Mod;
		return re;
	} 
}ans, st;
ll read() {
	ll X = 0, p = 1; char c = getchar();
	for (; c > '9' || c < '0'; c = getchar()) if (c == '-') p = -1;
	for (; c <= '9' && c >= '0'; c = getchar()) X = X * 10 + c - '0';
	return X * p;
}
ll dfs(int pos, int sta, bool limit) {
	if (pos == -1) return 1;
	if (!limit && dp[pos][sta] != -1) return dp[pos][sta];
	int up = limit ? digit[pos] : 1;
	ll sum = 0;
	for (int i = 0; i <= up; i++) {
		if (sta && i == 1) continue;
		sum += dfs(pos - 1, i == 1, limit && i == digit[pos]);
	}
	if (!limit) dp[pos][sta] = sum;
	return sum;
}
ll solve(ll x) {
	int cnt = 0;
	while (x) {
		digit[cnt++] = x % 2, x /= 2;
	}
	ll ans = dfs(cnt - 1, 0, true);
	return ans;
}
void print(ll x) {
	memset(dp, -1, sizeof(dp));
	memset(st.a, 0, sizeof(st.a));
	memset(ans.a, 0, sizeof(ans.a));
	st.a[1][1] = st.a[1][2] = st.a[2][1] = 1;
	ans.a[1][1] = 2, ans.a[1][2] = 1;
	printf("%lld\n", solve(x) - 1); // 因为多计算了个0也是解,但题目要求正整数 
	x -= 1;
	for (; x; x >>= 1, st = st * st) 
		if (x & 1) ans = ans * st;
	printf("%lld\n", (ans.a[1][1] % Mod + Mod) % Mod);
}
int main() {
	T = read();
	while (T--) print(read());
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值