zoukankan      html  css  js  c++  java
  • 「LibreOJ NOI Round #1」北校门外的回忆(找性质+倍增+线段树)

    https://loj.ac/problem/510

    对于(n<=100000)的,(x)(x+low(x))连边,构成内向森林,那么每次就是修改一条到根的链,和询问一个点。

    对于(n)很大的,我们要思考更深层的东西。

    考虑为什么(k>2)时add循环很多次。

    比如(k=3),一开始最低位(=1),每次(*2~mod~k),会在1、2、1、2循环,就达到了(O(n))

    考虑k是奇数,会一直在一位循环,(x)(x+low(x))形成的森林其实是若干条链,因为不可能有其它的边插进来有(有2的逆元)。

    (k)不是奇数时,不仅可能加着加着这一位变成了0,跳到更高的位,也可能一个点有多条入边。

    事实上,如果只考虑含有2的指数(>=k含有的),就是若干条链了,这相当于k的2的部分无用了。

    而在一位上,最多乘个log次含有2的指数就(>=k含有的了),要不就中途就跳到下一层了。

    所以,从任何一点,走(log)次就会走到链上。

    链上可以用动态开点线段树维护,问题是怎么找到一个链的开头?

    倍增:预处理(f[i][j])表示,某一位是i,要使高位(-2^j),会跳到哪里,每次跳即可。

    时间复杂度:(log^2)

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    int calc(int x) {
    	int s = 0;
    	while(x % 2 == 0) x /= 2, s ++;
    	return s;
    }
    
    const int M = 19260817;
    const int N = (2e5 + 5) * 31;
    
    struct hash {
    	int fi[M];
    	int h[N]; int nt[N], c[N], tot;
    	int& operator[] (int n) {
    		int y = n % M;
    		for(int p = fi[y]; p; p = nt[p])
    			if(h[p] == n) return c[p];
    		nt[++ tot] = fi[y], h[tot] = n, fi[y] = tot;
    		return c[tot];
    	}
    	int find(int n) {
    		int y = n % M;
    		for(int p = fi[y]; p; p = nt[p])
    			if(h[p] == n) return c[p];
    		return 0;
    	}
    } f, rt;
    
    #define i0 t[i].l
    #define i1 t[i].r
    struct tree {
    	int l, r, x;
    } t[N]; int tt;
    int pl, pr, px;
    void add(int &i, int x, int y) {
    	if(y < pl || x > pr) return;
    	if(!i) i = ++ tt;
    	t[i].x ^= px;
    	if(x == y) return;
    	int m = x + y >> 1;
    	add(i0, x, m); add(i1, m + 1, y);
    }
    void ft(int i, int x, int y) {
    	if(!i || y < pl || x > pr) return;
    	if(x >= pl && y <= pr) { px ^= t[i].x; return;}
    	int m = x + y >> 1;
    	ft(i0, x, m); ft(i1, m + 1, y);
    }
    
    int n, q, k, k2;
    
    int op, x, y;
    
    int w[32][200005], w0[200005];
    
    void build() {
    	ff(i, 1, k) {
    		int t = calc(i);
    		if(t >= k2) {
    			w0[i] = t > k2 ? w0[i / 2] : i;
    			w[0][i * 2 % k] = w0[i];
    		}
    	}
    	fo(j, 1, 31) ff(i, 1, k)
    		w[j][i] = w[j - 1][w[j - 1][i]];
    }
    
    int low(int x) {
    	int s = 1;
    	while(x % k == 0) s *= k, x /= k;
    	return x % k * s;
    }
    
    int low2(int x) {
    	int s = 1;
    	while(x % k == 0) s *= k, x /= k;
    	return x % k;
    }
    
    int qv(int x) {
    	int s = 1;
    	while(x % k == 0) s *= k, x /= k;
    	return s;
    }
    
    void add(int x, int y) {
    	while(x <= n) {
    		if(calc(low2(x)) >= k2) break;
    		f[x] ^= y;
    		x += low(x);
    	}
    	if(x > n) return;
    //	pp("%d %d
    ", x, y);
    	int l = low(x);
    	int v = qv(x);
    	int s = (x - l) / v / k;
    	int z = w0[l / v];
    	fd(i, 31, 0) if(s >> i & 1) z = w[i][z];
    	pl = pr = x; px = y;
    	add(rt[z * v], 1, n);
    }
    
    int sum(int x) {
    	int ans = 0;
    	for(; x; ) {
    		ans ^= f.find(x);
    		
    		int l = low(x);
    		if(calc(low2(x)) >= k2) {
    			int v = qv(x);
    			int s = (x - l) / v / k;
    			int z = w0[l / v];
    			fd(i, 31, 0) if(s >> i & 1) z = w[i][z];
    			pl = 1, pr = x, px = 0;
    			ft(rt.find(z * v), 1, n);
    			ans ^= px;
    		}
    		
    		x -= l;
    	}
    	return ans;
    }
    
    int main() {
    	scanf("%d %d %d", &n, &q, &k);
    	k2 = calc(k);
    	build();
    	fo(i, 1, q) {
    		scanf("%d %d", &op, &x);
    		if(op == 1) {
    			scanf("%d", &y);
    			add(x, y);
    		} else {
    			pp("%d
    ", sum(x));
    		}
    	}
    }
    
  • 相关阅读:
    vite的使用
    webpack--Plugin
    webpack--loader
    uniapp 或小程序,通过蓝牙连接设备, 给设备配网
    每一项与之前一项相加
    vue使用vue-fullpage
    React基礎
    无间隙循环轮播效果
    Wow.js动画效果
    正则表达式
  • 原文地址:https://www.cnblogs.com/coldchair/p/12669041.html
Copyright © 2011-2022 走看看