suxxsfe

一言(ヒトコト)

bzoj3813 奇数国

Description

在一片美丽的大陆上有\(100000\)个国家,记为\(1\)\(100000\)。这里经济发达,有数不尽的账房,并且每个国家有一个银行。
某大公司的领袖在这\(100000\)个银行开户时都存了\(3\)大洋,他惜财如命,因此会不时地派小弟 GFS 清点一些银行的存款或者让 GFS 改变某个银行的存款。
该村子在财产上的求和运算等同于我们的乘法运算,也就是说领袖开户时的存款总和为\(3^{100000}\)。这里发行的软妹面额是最小的\(60\)个素数(\(p_1=2,p_2=3,\cdots,p_60=281\)),任何人的财产都只能由这\(60\)个基本面额表示,即设某个人的财产为 \(fortune\)(正整数),则\(fortune=p_1^{k_1}p_2^{k_2}\cdots p_{60}^{K_{60}}\)

领袖习惯将一段编号连续的银行里的存款拿到一个账房去清点,为了避免 GFS 串通账房叛变,所以他不会每次都选择同一个账房。GFS 跟随领袖多年已经摸清了门路,知道领袖选择账房的方式。如果领袖选择清点编号在\([a,b]\)内的银行财产,他会先对\([a,b]\)的财产求和(计为\(product\)),然后在编号属于\([1,product]\)的账房中选择一个去清点存款,检验自己计算是否正确同时也检验账房与 GFS 是否有勾结。
GFS 发现如果某个账房的编号\(number\)\(product\)相冲,领袖绝对不会选择这个账房。怎样才算与\(product\)不相冲呢?若存在整数\(x,y\)使得\(number\cdot x+product\cdot y=1\),那么我们称\(number\)\(product\)不相冲,即该账房有可能被领袖相中。

当领袖又赚大钱了的时候,他会在某个银行改变存款,这样一来相同区间的银行在不同的时候算出来的\(product\)可能是不一样的,而且领袖不会在某个银行的存款总数超过\(1000000\)
现在GFS预先知道了领袖的清点存款与变动存款的计划,想请你告诉他,每次清点存款时领袖有多少个账房可以供他选择,当然这个值可能非常大,GFS只想知道对\(19961993\)取模后的答案。

Input

第一行一个整数\(x\le 10^5\)表示领袖清点和变动存款的总次数。
接下来\(x\)行,每行\(3\)个整数\(a_i,b_i,c_i\)

  • \(a_i\)\(0\)时表示该条记录是清点计划,领袖会清点\(b_i\)\(c_i\)的银行存款,你需要对该条记录计算出 GFS 想要的答案。
  • \(a_i\)\(1\)时表示该条记录是存款变动,你要把银行\(b_i\)的存款改为\(c_i\),不需要对该记录进行计算。

Output

输出若干行,每行一个数,表示那些年的答案。

Sample Input

6
0 1 3
1 1 5
0 1 3
1 1 7
0 1 3
0 2 3

Sample Output

18
24
36
6

explanation

初始化每个国家存款都为\(3\);

\(1\)\(3\)\(product\)\(27\)\([1,27]\)\(27\)不相冲的有\(18\)个数;
\(1\)的存款变为\(5\);
\(1\)\(3\)\(product\)\(45\)\([1,45]\)\(45\)不相冲的有\(24\)个数;
\(1\)的存款变为\(7\);
\(1\)\(3\)\(product\)\(63\)\([1,63]\)\(63\)不相冲的有\(36\)个数;
\(2\)\(3\)\(product\)\(9\)\([1,9]\)\(9\)不相冲的有$64个数。


欧拉函数+线段树

题目里一堆废话,一开始看了样例解释才懂,所以也复制上来了
裴蜀定理告诉我们,题里那个存在整数\(x,y\)使得\(number\cdot x+product\cdot y=1\)成立的条件,其实就是\(\gcd(number,product)=1\)
又由于\(number\le product\),所以其实求的就是\(\varphi(product)\)
所以题目即为:求区间乘积的\(\varphi\)函数取模,并支持单点修改


肯定是要用线段树,维护两个值,一个是区间乘积取模结果,一个是区间乘积有哪些质因数
由于题目告诉我们,不同质因数最多\(60\)个,所以我们用一个\(\texttt{long long}\)的第\(i-1\)个二进制位表示有没有第\(i\)小的质因数
然后合并信息的时候或一下就行,线段树压位常规套路

那么求\(\varphi\)就用到这个公式

\[\varphi(n)=n\prod \frac{p_i-1}{p_i},p_i\in prime,p_i\mid n \]

所以预处理一下逆元和质数就行了
记得左移的时候要写1ll

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define mod 19961993ll
int n;
struct tr{
	tr *ls,*rs;
	LL x,p;
}dizhi[200006],*root=&dizhi[0];
int tot;
int prime[305],notprime[305];
LL inv[305];
inline void pre(){
	inv[1]=1;
	for(reg int i=2;i<=300;i++){
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		if(notprime[i]) continue;
		prime[++prime[0]]=i;
		for(reg int j=i+i;j<=300;j+=i) notprime[j]=1;
	}
}
void build(tr *tree,int l,int r){
	if(l==r) return tree->x=3,tree->p=2,void();
	int mid=(l+r)>>1;
	tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
	build(tree->ls,l,mid);build(tree->rs,mid+1,r);
	tree->x=tree->ls->x*tree->rs->x%mod;
	tree->p=tree->ls->p|tree->rs->p;
}
void change(tr *tree,int l,int r,int pos,int x){
	if(l==r){
		tree->x=x;
		tree->p=0;
		for(reg int i=1;i<=prime[0];i++)
			if(!(x%prime[i])) tree->p|=1ll<<(i-1);
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) change(tree->ls,l,mid,pos,x);
	else change(tree->rs,mid+1,r,pos,x);
	tree->x=tree->ls->x*tree->rs->x%mod;
	tree->p=tree->ls->p|tree->rs->p;
}
void qq(tr *tree,int l,int r,int ql,int qr,LL &x,LL &p){
	if(ql<=l&&r<=qr) return x=x*tree->x%mod,p|=tree->p,void();
	int mid=(l+r)>>1;
	if(ql<=mid) qq(tree->ls,l,mid,ql,qr,x,p);
	if(qr>mid) qq(tree->rs,mid+1,r,ql,qr,x,p);
}
int main(){
	n=read();
	reg int a,b,c;
	pre();build(root,1,100000);
	while(n--){
		a=read();b=read();c=read();
		if(a) change(root,1,100000,b,c);
		else{
			LL x=1,p=0;
			qq(root,1,100000,b,c,x,p);
//				std::printf("x=%lld  p=%lld\n",x,p);
			for(reg int i=1;i<=prime[0];i++)
				if(p&(1ll<<(i-1))) x=(((x*(prime[i]-1))%mod)*inv[prime[i]])%mod;
			std::printf("%lld\n",x);
		}
	}
//		for(reg int i=1;i<=300;i++) std::printf("%d : %lld\n",i,inv[i]);
//		std::printf("prime[0]=%d",prime[0]);
	return 0;
}
posted @ 2020-04-07 23:57  suxxsfe  阅读(122)  评论(0编辑  收藏  举报