SSY20240805提高组T2题解

1 篇文章 0 订阅
1 篇文章 0 订阅

题干描述

题目描述

给定一个长度为 𝑛 的数组,第 𝑖 项为𝑎i​,你需要支持以下操作: 1,给定l,r,𝑥,将区间 [𝑙,𝑟] 中所有元素乘 𝑥。 2. 给定 𝑙,𝑟,查询区间 [𝑙,𝑟] 中所有元素之积的欧拉函数值,因为结果可能很大,你只需要输出答案对 10^9+ 7 取模的结果。 你共需进行 𝑞 次操作。

输入输出格式

输入:

第一行两个整数 𝑛,𝑞,分别代表数组的长度与操作的次数。 第二行 𝑛 个整数,代表数组内初始元素。 接下来 𝑞 行形式为 1 l r x 或 2 l r,含义如上所述。

输出:

输出 𝑞 行,每行一个整数表示对应询问的答案,你需要正确回答所有询问来获取该测试点的分数。

样例

输入数据1

4 4
2 4 6 7
2 1 4
1 2 3 3
1 3 4 4
2 3 4

输出数据1

96
576

输入数据2

5 7
252 292 6 122 8 
2 4 5
2 1 5
1 2 4 96
2 4 5
2 2 5
1 3 5 175
2 3 4

输出数据2

480
119439360
30720
223615137
448639678

数据范围

对于100%的数据 : n\leq10^5, q\leq10^5,1\leq a_{i}^{},x\leq300

题解

分析

首先,我们先要知道数论的一些基本知识

1. \varphi (n)=n\prod_{p|n\;\;(p\;is\;prime\;number)}^{}(1-\frac{1}{p})   (欧拉函数)

2. a^{p-1}\equiv 1(mod\;p)                                         (费马小定理)

这部分证明去问隔壁数竞吧,跟我们信竞没关系......

现在, 我们要通过公式1, 求出区间积的欧拉函数值

公式由两部分组成: n 与 1-\frac{1}{p}

对于n

我们知道, 初始值a[i]与后乘的数x\leq300, 而会对\varphi (n)产生贡献的只会是n的质因数, 小于300的质数只有62个

所以, 我们可以利用一个62位的bitset来记录第i个质因数是否在n中出现过, 利用线段树维护即可(乘积  和  是否出现的质数  都要维护)

对于1-(1/p)

 因为取模的原因, 我们需要预处理出所有质因数的逆元

利用费马小定理,可以推导出

a^{p-1}\equiv 1(mod\;p)   等价于   a^{-1}\equiv a^{p-2}(mod\;p)

同时, 1-\frac{1}{p}   也等价于   (p-1)p^{-1}

mod\;1e9+7的条件下, (p-1)p^{-1}\equiv (p-1)p^{1e9+7-2}(mod\;1e9+7)

最后把他们乘到一起, 再取模即可

优化

1.快速幂

2.预处理出300以内质数逆元, 300以内所有数包含的质因数

3.快读快写

4.祈求老师把时间开大一些

AC Code

#include <bits/stdc++.h>
using namespace std;
#define ls(x) x<<1
#define rs(x) x<<1|1
#define int long long
#define N 100086

const int mod=1e9 + 7;

int n,q;
int a[N],prim[70],vis[310];
int tot=0;
int mul[N<<2];
bitset<70> tag[N<<2];//tag表示当前区间(或节点)乘积是否含某质数 
bitset<70> lzt[N<<2];//tag的"lzt" 
bitset<70> y_num[310];
int to[310],in_e[70];
int lzm[N<<2];


void read(int &x) {
    char c;
    do {
        c = getchar();
    } while (c == ' ' || c == '\n');
    x = 0;
    int w = 1;
    if (c == '-') {
        w = -1;
        c = getchar();
    }
    do {
        x = (x << 1) + (x << 3) + c - '0';
        c = getchar();
    } while (c != ' ' && c != '\n');
    x *= w;
}
inline void wt(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) wt(x/10);
    putchar(x%10+'0');return;
}

int qpow(int a,int b){
    int ans=1;
    while(b!=0){
        if(b%2==1){
            ans=ans%mod*a%mod;
            ans%=mod;
        }
        a=a%mod*a%mod;
        b>>=1;
    }
    return ans;
}

void init(){
    for(int x=2;x<=300;x++){
        if(!vis[x]){
            prim[++tot]=x;
            to[x]=tot;
        }
        for(int y=1;x*prim[y]<=300;y++){
            vis[x*prim[y]]=1;
            if(x%prim[y]==0){
                break;
            }
        }
    }
    for(int x=2;x<=305;x++){
    	int xx=x;
    	for(int y=1;y<=62;y++){
    		if(xx%prim[y]==0){
    			while(xx%prim[y]==0){
    				xx/=prim[y];
				}
				y_num[x].set(y);
			}
		}
	}
	for(int x=1;x<=tot;x++){
		in_e[x]=qpow(prim[x],mod-2);
	}
}

void push_up(int now){
    tag[now]=  tag[ls(now)]  |  tag[rs(now)];
    mul[now]=  mul[ls(now)]  *  mul[rs(now)]%mod;
    mul[now]%=mod;
}

void build(int now,int l,int r){
	lzm[now]=1;
	lzt[now].reset();
	if(l==r){
		mul[now]=a[l];
		tag[now]=y_num[a[l]];
		return;
	}
	mul[now]=1;
	int mid=(l+r)>>1;
	build(ls(now),l,mid);
	build(rs(now),mid+1,r);
	push_up(now);
//	cout << l << ' ' << r << ' ' << tag[now] << '\n';
}

void push_down(int u,int l,int r){
	int mid=(l+r)>>1;
	if(l!=r){
		lzt[ls(u)]|=lzt[u];lzt[rs(u)]|=lzt[u];
		tag[ls(u)]|=lzt[u];tag[rs(u)]|=lzt[u];
		lzm[ls(u)]=lzm[ls(u)]*lzm[u]%mod;lzm[rs(u)]=lzm[rs(u)]*lzm[u]%mod;
        mul[ls(u)]=mul[ls(u)]  *  qpow(lzm[u],mid-l+1)  % mod;
        mul[rs(u)]=mul[rs(u)]  *  qpow(lzm[u],r-mid)    % mod;
	}
	lzm[u]=1;
	lzt[u].reset();
}

void update(int u,int l,int r,int fl,int fr,int MUL){
	if(fl<=l && fr>=r){
		mul[u]=mul[u]*qpow(MUL,r-l+1)%mod;
		lzm[u]=lzm[u]*MUL%mod;
		tag[u]|=y_num[MUL];
		lzt[u]|=y_num[MUL];
		return;
	}
	push_down(u,l,r);
	int mid=(l+r)>>1;
	if(fl<=mid){
		update(ls(u),l,mid,fl,fr,MUL);
	}
	if(fr>mid){
		update(rs(u),mid+1,r,fl,fr,MUL);
	}
	push_up(u);
}

int find(int u,int l,int r,int fl,int fr){
    if(fl<=l && r<=fr){
    	return mul[u];
	}
	push_down(u,l,r);
	int mid=(l+r)>>1;
	int ans=1;
	if(fl<=mid){
		ans=find(ls(u),l,mid,fl,fr)*ans%mod;
	}
	if(fr>mid){
		ans=find(rs(u),mid+1,r,fl,fr)*ans%mod;
	}
	return ans;
}

bitset<70> find_pri(int u,int l,int r,int fl,int fr){
    if(fl<=l && r<=fr){
    	return tag[u];
	}
	push_down(u,l,r);
	int mid=(l+r)>>1;
	bitset<70> ans;
	ans.reset();
	if(fl<=mid){
		ans=find_pri(ls(u),l,mid,fl,fr)|ans;
	}
	if(fr>mid){
		ans=find_pri(rs(u),mid+1,r,fl,fr)|ans;
	}
	return ans;
}

signed main(){
//	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    init();
//    cin >> n >> q;
	scanf("%lld%lld",&n,&q);
//	read(n);
//	read(q);
//    for(int x=1;x<=32;x++){
//    	cout << prim[x] << ' ';
//	}
//	cout << '\n';
    for(int x=1;x<=n;x++){
		scanf("%lld",&a[x]);
//		read(a[x]);
	}
	build(1,1,n);
	for(int x=1;x<=q;x++){
		int op,l,r,X;
		scanf("%lld%lld%lld",&op,&l,&r);
//		read(op);read(l);read(r);
		if(op==1){
			scanf("%lld",&X);
//			read(X);
			update(1,1,n,l,r,X);
		}else{
			bitset<70> ans=find_pri(1,1,n,l,r);
			int Ans=find(1,1,n,l,r);
//			cout << ans << '\n';
			for(int y=1;y<=62;y++){
				if(ans[y]==1){
					Ans=Ans*(prim[y]-1)%mod*in_e[y]%mod;
				}
			}
			wt(Ans);
			printf("\n");
		}
	}
    return 0;
}

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值