• LOJ 510: 「LibreOJ NOI Round #1」北校门外的回忆


    题目传送门:LOJ #510

    题意简述:

    给出一个在 (K) 进制下的树状数组,但是它的实现有问题。

    形式化地说,令 (mathrm{lowbit}(x)) 为在 (K) 进制下的 (x) 的最低非零位的值,例如 (mathrm{lowbit} !left( 230_{(5)} ight)! = 30_{(5)})

    add(x, v) 操作执行的是:
    (x le n) 时,执行 s[x] ^= v,并将 (x) 变为 (x + mathrm{lowbit}(x)),并继续循环。

    query(x) 操作执行的是:
    (ans) 的初始值为 (0)
    (x > 0) 时,执行 ans ^= s[x],并将 (x) 变为 (x - mathrm{lowbit}(x)),并继续循环。
    最终返回 (ans)

    题解:

    不同于正确写法,add 操作执行时的运算次数不是 (mathcal O ( K log_K n )) 的,而是 (mathcal O (n)) 的,观察何时它的复杂度会发生变化:
    (K = 3, x = 1) 时,(x) 的变化为(十进制下):(1 o 2 o 4 o 5 o 7 o 8 o 10 o cdots)

    可以发现,(x)(K) 进制下的最低非零位的位置始终不变,而且最低非零位的值陷入了 (1, 2, 1, 2, ldots) 的循环。

    对于不同的 (K),我们观察特定的最低非零位的值的变化,发现有的最终变成了 (0),即这一位不再是非零的,而有的落入了循环中。

    很显然,这其实就是在函数 (f(x) = 2x mod K) 对应的图上转移的过程,这张图是一个基环内向森林,其中有一个连通分量的根是 (0 o 0) 的自环,与 (0) 相连的点最终都会提升最低非零位。

    数论告诉我们,每棵基环内向树的深度都不会超过 (log_2 K),如果所在的是根为 (0) 的基环内向树,最低位也只会变化 (log_K n) 次,总次数为 (log_2 K imes log_K n = log_2 n),所以这部分暴力跳即可。

    当最终落到了不为 (0) 的环上时,参考上面的例子,会形成一条无限向后延伸的链,但是它是一个相邻位置的差有循环节的链。
    如果将每条链预处理出来,就可以对每条链开一个树状数组,以维护后缀加,单点查的操作。
    但是这里值域太大,无法直接预处理,所以考虑用倍增的方法直接求出该点在链上的编号,即 (x) 是在当前链上的第几个位置,就可以直接进行树状数组操作了(当然此时值域仍然是极大的,但是可以通过哈希的方法将改动的位置映射到小数组中)。

    询问的时候同理,query(x) 只会变化 (log_K n) 次,所以可以每次直接查,首先判断 (x) 是否在链上,如果不是直接修改 (ans) 即可,否则需要在当前链对应的树状数组上进行单点查的操作(前面的修改对应的是后缀加),做个差分就可以变成单点加、前缀查了,就是经典的树状数组操作,对于大值域同理采用哈希的方法即可。

    下面是代码,假设哈希的时间复杂度为 (mathcal O (1)),则总时间复杂度为 (mathcal O (K log n + q log_K n log n))

    #include <cstdio>
    
    const int HASH = 19260817, NUMS = 10000005;
    int h[HASH], nxt[NUMS], to[NUMS], tot;
    inline int Hasher(int val, int typ) {
    	int cyp = (val ^ val >> 1) % HASH;
    	for (int i = h[cyp]; i; i = nxt[i]) if (to[i] == val) return i;
    	return typ ? nxt[++tot] = h[cyp], to[tot] = val, h[cyp] = tot : 0;
    }
    
    typedef long long LL;
    const int MK = 200005;
    
    int N, K, Q, V[6], C;
    inline void lowbit(int x, int &y, int &z) {
    	int s = 1, t = C;
    	while (t) { if (x % V[t] == 0) x /= V[t], s *= V[t]; --t; }
    	y = x % K, z = y * s;
    }
    int ltrs[MK][30], ntrs[MK][30];
    LL lsum[MK][30], nsum[MK][30];
    int arr[NUMS];
    
    int main() {
    	scanf("%d%d%d", &N, &Q, &K);
    	for (LL x = K; x <= N; x *= x) V[++C] = x;
    	for (int i = 1; i < K; ++i) if ((i & -i) >= (K & -K))
    		ltrs[i * 2 % K][0] = lsum[i * 2 % K][0] = i,
    		ntrs[i][0] = i * 2 % K, nsum[i][0] = i;
    	for (int j = 0; j < 29; ++j)
    		for (int i = 1; i < K; ++i) if ((i & -i) >= (K & -K))
    			ltrs[i][j + 1] = ltrs[ltrs[i][j]][j],
    			lsum[i][j + 1] = lsum[i][j] + lsum[ltrs[i][j]][j],
    			ntrs[i][j + 1] = ntrs[ntrs[i][j]][j],
    			nsum[i][j + 1] = nsum[i][j] + nsum[ntrs[i][j]][j];
    	for (int i = 1; i <= Q; ++i) {
    		int op, x, y, z, v, d;
    		scanf("%d%d", &op, &x);
    		if (op == 1) {
    			scanf("%d", &v);
    			while (x <= N) {
    				lowbit(x, y, z);
    				if ((y & -y) >= (K & -K)) {
    					int id = 0, X = x, Y = y, w = z / y;
    					for (int j = 29; j >= 0; --j)
    						if (lsum[Y][j] < X / w)
    							id |= 1 << j,
    							X -= lsum[Y][j] * w,
    							Y = ltrs[Y][j];
    					++id;
    					for (int j = 0; x <= N; ++j) if (id >> j & 1) {
    						arr[Hasher(x, 1)] ^= v;
    						x += nsum[y][j] * w;
    						y = ntrs[y][j];
    						id += 1 << j;
    					}
    					break;
    				}
    				arr[Hasher(x, 1)] ^= v;
    				x += z;
    			}
    		} else {
    			int Ans = 0;
    			while (x) {
    				lowbit(x, y, z);
    				if ((y & -y) >= (K & -K)) {
    					int id = 0, X = x, Y = y, w = z / y;
    					for (int j = 29; j >= 0; --j)
    						if (lsum[Y][j] < X / w)
    							id |= 1 << j,
    							X -= lsum[Y][j] * w,
    							Y = ltrs[Y][j];
    					++id;
    					X = x, Y = y;
    					for (int j = 0; id; ++j, id >>= 1) if (id & 1) {
    						if ((d = Hasher(X, 0))) Ans ^= arr[d];
    						X -= lsum[Y][j] * w;
    						Y = ltrs[Y][j];
    					}
    				} else if ((d = Hasher(x, 0))) Ans ^= arr[d];
    				x -= z;
    			}
    			printf("%d
    ", Ans);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    PHP Smarty模板的安装
    百度地图API使用方法详解
    总结的一些微信API接口
    WePayUI 快快尝鲜体验
    Python发送邮件
    Python网络编程
    python 内置函数
    Python操作数据库
    Python操作excel
    python之函数
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/LOJ510.html
Copyright © 2020-2023  润新知