zoukankan      html  css  js  c++  java
  • [bzoj4026]dC Loves Number Theory_主席树_质因数分解_欧拉函数

    dC Loves Number Theory

    题目大意:dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯竭的水题资源。 给定一个长度为 n的正整数序列A,有q次询问,每次询问一段区间内所有元素乘积的φ(φ(n)代表1~n 中与n互质的数的个数) 。由于答案可能很大,所以请对答案 mod 10^6 + 777。 (本题强制在线,所有询问操作的l,r都需要 xor上一次询问的答案 lastans,初始时,lastans = 0)

    数据范围:1<=N<=50000,1<=Q<=100000,1<=Ai<=10^6。


    题解:

    完全不会.....

    听了$lijinnn$讲了才明白。

    有一个非常非常重要的性质,如果没发现的话根本做不了。

    我们考虑暴力怎么搞:每次全扫一遍,然后统计答案。

    这也太暴力了....我们优化优化。

    这样,每个位置开一个长度最多为$7$的数组表示当前位置的质因子因为我们知道:

    $varphi(n) = n*prodlimits_{p_i|n} frac{p_i - 1}{p_i}$。

    这复杂度是$O(n^2 imes 7)$的。

    再优化优化???

    发现,对于每个左端点,我可以维护处每个质因子在当前左端点右边最靠左出现的位置。

    就是先扫一遍整个数组,先求出来左端点在$1$时,每个质数最左出现的位置。

    接着每次,左端点往右动,更新当前左端点所在位置的值的所有质因子的答案即可,这样再查询就是在左端点对应的数组上查就好了,这是$O(n)$的。

    好,现在的复杂度到了$O(n^2)$,重点就是维护每个质因子在不超过左端点的情况下,最左的位置。

    那正解不就出来了么!

    我们发现,左端点每动一次,更改的值只有最多$7$个,剩下的值都是相同的。

    什么东西可以维护这个?就是一大堆都一样,只有几个不同

    主席树呀,我们对每个左端点开一棵主席树,直接维护刚才说的数组。

    咋维护呢?

    就是对于第$i$颗主席树,如果有一个质数$p$在$i$左侧最左的位置是$j$,那就在主席树$j$的位置$*=frac{p - 1}{p}$,然后主席树节点维护所支配区间所有数乘积。

    但是,每个点单独质因数分解是$O(nsqrt{a_i})$的,我们通过预处理质数,然后枚举质数的方式就好啦,复杂度是调和级数$O(nln n)$。

    主席树的复杂度是$O(nlogn)$,每次$O(loglogn)$次,总复杂度是$O(nlognloglogn + nln n)$。

    代码

    #include <bits/stdc++.h>
    
    #define N 50010 
    
    using namespace std;
    
    const int mod = 1000777 ;
    
    typedef long long ll;
    
    vector <int> v[1000010 ];
    
    int prime[1000010 ], cnt;
    
    bool vis[1000010 ];
    
    int pre[1000010 ], bfr[N], a[N];
    
    int ls[N * 200], rs[N * 200], sum[N * 200], root[N * 200], tot;
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
    
    int rd() {
    	int x = 0, f = 1;
    	char c = nc();
    	while (c < 48) {
    		if (c == '-')
    			f = -1;
    		c = nc();
    	}
    	while (c > 47) {
    		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    	}
    	return x * f;
    }
    
    int qpow(int x, int y) {
    	int ans = 1;
    	while (y) {
    		if (y & 1)
    			ans = (ll)ans * x % mod;
    		y >>= 1;
    		x = (ll)x * x % mod;
    	}
    	return ans;
    }
    
    void init() {
    	for (int i = 2; i <= 1000000; i ++ ) {
    		if (!vis[i])
    			prime[ ++ cnt] = i;
    		for (int j = 1; j <= cnt && (ll)i * prime[j] <= 1000000; j ++ ) {
    			vis[i * prime[j]] = true;
    			if (i % prime[j] == 0) {
    				break;
    			}
    		}
    	}
    }
    
    void update(int x, int val, int l, int r, int pre, int &p) {
    	p = ++tot;
    	ls[p] = ls[pre];
    	rs[p] = rs[pre];
    	sum[p] = (ll)sum[pre] * val % mod;
    	if (l == r) {
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if (x <= mid)
    		update(x, val, l, mid, ls[pre], ls[p]);
    	else
    		update(x, val, mid + 1, r, rs[pre], rs[p]);
    }
    
    int query(int x, int y, int l, int r, int p) {
    	if (!p) {
    		return 1;
    	}
    	if (x <= l && r <= y) {
    		return sum[p];
    	}
    	int ans = 1, mid = (l + r) >> 1;
    	if (x <= mid)
    		ans = (ll)ans * query(x, y, l, mid, ls[p]) % mod;
    	if (mid < y)
    		ans = (ll)ans * query(x, y, mid + 1, r, rs[p]) % mod;
    	return ans;
    }
    
    int main() {
    	int n = rd(), m = rd();
    	for (int i = 1; i <= n; i ++ ) {
    		a[i] = rd();
    	}
    
    	bfr[0] = 1;
    	for (int i = 1; i <= n; i ++ ) {
    		bfr[i] = (ll)bfr[i - 1] * a[i] % mod;
    	}
    
    	init();
    	for (int i = 1; i <= cnt; i ++ ) {
    		for (int j = prime[i]; j <= 1000000; j += prime[i]) {
    			v[j].push_back(i);
    		}
    	}
    
    	// cout << prime[cnt] << endl ;
    
    	// for (int i = 1; i <= n; i ++ ) {
    	// 	int len = v[a[i]].size();
    	// 	for (int j = 0; j < len; j ++ ) {
    	// 		printf("%d ",prime[v[a[i]][j]]);
    	// 	}
    	// 	puts("");
    	// }
    
    	sum[0] = 1;
    
    	for (int i = 1; i <= n; i ++ ) {
    		int len = v[a[i]].size();
    		for (int j = 0; j < len; j ++ ) {
    			if (pre[v[a[i]][j]]) {
    				update(pre[v[a[i]][j]], (ll)prime[v[a[i]][j]] * qpow(prime[v[a[i]][j]] - 1, mod - 2) % mod, 1, n, (root[i] ? root[i] : root[i - 1]), root[i]);
    			}
    			update(i, (ll)(prime[v[a[i]][j]] - 1) * qpow(prime[v[a[i]][j]], mod - 2) % mod, 1, n, (root[i] ? root[i] : root[i - 1]), root[i]);
    			// printf("Shit %d
    ",v[a[i]][j]);
    			pre[v[a[i]][j]] = i;
    		}
    	}
    
    	// cout << sum[root[1]] << endl ;
    	// cout << (ll)sum[root[1]] * 3 % mod * qpow(2, mod - 2) % mod << endl ;
    
    	// cout << sum[root[4]] << endl ;
    	// cout << (ll)sum[root[4]] * 2 * 6 * 4 % mod * qpow(3, mod - 2) % mod * qpow(2, mod - 2) % mod * qpow(5, mod - 2) % mod * qpow(7, mod - 2) % mod << endl ;
    
    	// puts("Fuck");
    
    	int lastans = 0;
    	for (int i = 1; i <= m; i ++ ) {
    		int x = rd(), y = rd();
    		x ^= lastans, y ^= lastans;
    		if (x > y) {
    			lastans = 1;
    			puts("0");
    			continue;
    		}
    		// cout << (ll)bfr[y] * qpow(bfr[x - 1], mod - 2) % mod << endl ;
    		lastans = (ll)bfr[y] * qpow(bfr[x - 1], mod - 2) % mod * query(x, y, 1, n, root[y]) % mod;
    		printf("%d
    ", lastans);
    	}
    	return 0;
    }
    

    小结:真的是好题啊,重要的是那个性质能不能发现。主席树的运用也非常巧妙。

  • 相关阅读:
    【React Native】某个页面禁用物理返回键
    【React Native】DeviceEventEmitter监听通知及带参数传值
    转载【React Native代码】手写验证码倒计时组件
    【React Native】 中设置 APP 名称、应用图标、为安卓添加启动图
    【React Native错误集】* What went wrong: Execution failed for task ':app:installDebug'.
    【React Native错误集】Import fails with "Failed to execute 'ImportScripts' on 'WorkerGlobalScope'"
    【React Native错误集】Android error “Could not get BatchedBridge, make sure your bundle is packaged properly” on start of app
    「React Native笔记」在React的 setState 中操作数组和对象的多种方法(合集)
    【React Native】Error: Attribute application@allowBackup value=(false) from AndroidManifest.xml
    坚果云如何使用二次验证码/谷歌身份验证器/两步验证/虚拟MFA?
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11255815.html
Copyright © 2011-2022 走看看