2015北京区域赛 K.A Math Problem
UVA提交会RE,no why!
题意
定义一个集合,集合的函数映射为:
定义 [1,n] 中的任意数字为 si ,然后规定 g(x) 为 [1,n] 中令 f(si)%k==x 的 si 的个数.
求 g(x) 异或的结果
解题思路
将
(2)
简化得到
3fn×(f2n+1−f2n)=f2n
,由于
f1=1
,所以
f2n+1−f2n>0
.
然后将
fan<6fn
带入
(2)
中可以得到
3fn×(f2n+1−f2n)<6fn
得到
f2n+1−f2n<2
,所以
f2n+1−f2n=1
,所以
f2n+1=f2n+1
,将这个式子带入
(2)
中可以得到如下俩个等式
接下来我们可以发现,对于当前这个数我们可以进行两种操作:
-
m
为奇数的时候,一定可以表达为
fm=3×fm−12+1 - 偶数时则是 fm=3×fm2+1 .
当我们第一个操作完后,接下来我们要进行第一步操作还是第二步操作取决于当前的奇偶,即二进制当前位是
1
还是
其中 2_bitx 表示 n 转换为二进制时的每一位.
然后我们就可以用数位DP来处理了,数组定义如下:
bit[x]
:用来保存
n
<script type="math/tex" id="MathJax-Element-5455">n</script>的每一位
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int WS = 64 + 5;
const int REM = 65537 + 5;
LL n, dp[WS][REM], P[WS], Rsum[REM];
bool vis[WS];
int bit[WS], sz, k;
void pinit() {
sz = 0;
memset(dp, 0, sizeof(dp));
memset(Rsum, 0, sizeof(Rsum));
memset(vis, false, sizeof(vis));
P[0] = 1;
for(int i = 1; i < WS; i ++) {
P[i] = P[i - 1] * 3 % k;
}
}
void DFS(int pos, int remainder, int limit) {
if(pos < 0) {
Rsum[remainder] ++;
return ;
}
//此处不再是用常规的dp[pos]...去标记是否访问的原因是,当前状态具有多种,即reaminder对余数个数的影响不同
if(!limit && vis[pos]) {
remainder = remainder * P[pos + 1] % k;
for(int i = 0; i < k; i ++) {
Rsum[(remainder + i) % k] += dp[pos][i];
}
return;
}
int ends = limit ? bit[pos] : 1;
for(int i = 0; i <= ends; i ++) {
DFS(pos - 1, (remainder * 3 + i) % k, limit && i == ends);
}
if(!limit) {
vis[pos] = true;
for(int i = 0; i < k; i ++) {
dp[pos][i] = Rsum[i];//当前位置的状态即是Rsum[i]
}
}
}
void solve() {
pinit();
int len = 0;
while(n) {
bit[len ++] = n % 2;
n >>= 1LL;
}
if(len == 0) len ++;
DFS(len - 1, 0, 1);
Rsum[0] --;//清除0对数据的影响
LL ans = 0;
for(int i = 0; i < k; i ++) {
ans ^= Rsum[i];
}
printf("%lld\n", ans);
}
int T;
int main() {
scanf("%d", &T);
while(T --) {
scanf("%lld%d", &n, &k);
solve();
}
return 0;
}