• 【CF594D】REQ


    题目

    题目链接:https://codeforces.com/problemset/problem/594/D
    给出一个长度为 \(n\) 的数列,\(Q\) 次询问,每次询问给出 \(l,r\),需要求出 \(\varphi(\prod ^{r}_{i=l}a[i])\bmod p\)
    \(n,Q\leq 200000,a[i]\leq 10^6\)

    思路

    考虑到一个数 \(x\) 的质因子不会超过 \(\log x\) 个,所以可以离线按照询问的右端点排序,记录每一个质因子最后出现的位置。
    按照右端点排序后,我们对于一个询问 \([l_i,r_i]\),将 \((r_{i-1},r_i]\) 的数分别求质因子,然后找到上一个拥有这个质因子的数,用一个数据结构维护一下,保证对于每一个质因子 \(p\),它的贡献都算在现在询问到的区间的最后一个含有 \(p\) 质因子的数字上。而一个数 \(p\) 的贡献是 \(\frac{p-1}{p}\),所以我们需要一个支持单调修改、区间查询乘积的数据结构,线段树和树状数组都可以的。
    如何快速处理出一个数的质因子?在线性筛的时候,我们求出了每个数的最小质因子 \(v\),那么就不断让 \(a[i]\) 除以 \(v[a[i]]\) 即可。
    讲的似乎很模糊,看一下这部分代码

    for (;j<=ask[i].r;j++)
    	for (int k=a[j];k>1;)
    	{
    		int d=v[k];  //求这一位的质因子
    		if (last[d]) seg.update(1,last[d],d);
    		if (last[d]) seg.update(1,last[d],inv[d-1]);  //在线段树中将上一个含有这个质因子的位置的贡献消除
    		last[d]=j;
    		seg.update(1,j,inv[d]);
    		seg.update(1,j,d-1);  //维护该质因子的贡献
    		while (k%d==0) k/=d;
    	}
    

    那么整个问题就迎刃而解了。需要预处理逆元,否则时间复杂度多一个 \(\log n\)
    时间复杂度 \(O(n\log n\log a[i])\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N=200010,M=1e6+10,MOD=1e9+7;
    int n,Q,m,a[N],last[M],v[M],prm[M],inv[M],ans[N];
    
    struct Query
    {
    	int l,r,id;
    }ask[N];
    
    bool cmp(Query x,Query y)
    {
    	return x.r<y.r;
    }
    
    void findprm(int k)
    {
    	for (int i=2;i<=k;i++)
    	{
    		if (!v[i]){v[i]=i; prm[++m]=i;}
    		for (int j=1;j<=m;j++)
    		{
    			if (1LL*prm[j]*i>k || v[i]<prm[j]) break;
    			v[prm[j]*i]=prm[j];
    		}
    	}
    }
    
    ll fpow(ll x,ll k,ll p)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%p)
    		if (k&1) ans=ans*x%p;
    	return ans;
    }
    
    struct Seg
    {
    	int l[N*4],r[N*4];
    	ll mul[N*4];
    	
    	void pushup(int x)
    	{
    		mul[x]=mul[x*2]*mul[x*2+1]%MOD;
    	}
    	
    	void build(int x,int ql,int qr)
    	{
    		l[x]=ql; r[x]=qr;
    		if (ql==qr)
    		{
    			mul[x]=a[ql];
    			return;
    		}
    		int mid=(ql+qr)>>1;
    		build(x*2,ql,mid); build(x*2+1,mid+1,qr);
    		pushup(x);
    	}
    	
    	void update(int x,int k,int val)
    	{
    		if (l[x]==k && r[x]==k)
    		{
    			mul[x]=mul[x]*val%MOD;
    			return;
    		}
    		int mid=(l[x]+r[x])>>1;
    		if (k<=mid) update(x*2,k,val);
    			else update(x*2+1,k,val);
    		pushup(x);
    	}
    	
    	ll query(int x,int ql,int qr)
    	{
    		if (l[x]==ql && r[x]==qr)
    			return mul[x];
    		int mid=(l[x]+r[x])>>1;
    		if (qr<=mid) return query(x*2,ql,qr);
    		else if (ql>mid) return query(x*2+1,ql,qr);
    		else return query(x*2,ql,mid)*query(x*2+1,mid+1,qr)%MOD;
    	}
    }seg;
    
    int main()
    {
    	findprm(M-10);
    	inv[0]=1;
    	for (int i=1;i<=M-10;i++)
    		inv[i]=fpow(i,MOD-2,MOD);
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	seg.build(1,1,n);
    	scanf("%d",&Q);
    	for (int i=1;i<=Q;i++)
    	{
    		scanf("%d%d",&ask[i].l,&ask[i].r);
    		ask[i].id=i;
    	}
    	sort(ask+1,ask+1+Q,cmp);
    	for (int i=1,j=1;i<=Q;i++)
    	{
    		for (;j<=ask[i].r;j++)
    			for (int k=a[j];k>1;)
    			{
    				int d=v[k];
    				if (last[d]) seg.update(1,last[d],d);
    				if (last[d]) seg.update(1,last[d],inv[d-1]);
    				last[d]=j;
    				seg.update(1,j,inv[d]);
    				seg.update(1,j,d-1);
    				while (k%d==0) k/=d;
    			}
    		ans[ask[i].id]=seg.query(1,ask[i].l,ask[i].r);
    	}
    	for (int i=1;i<=Q;i++)
    		printf("%d\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    [转]web串口调试助手,浏览器控制串口设备
    [转]WEB页获取串口数据
    [转]js串口通信 调用MSCOMM32控件 链接电子秤
    [转]C# serialPort 串口接收中this.Invoke的使用
    [转]C#串口通信 SerialPort类
    [转]How to display the data read in DataReceived event handler of serialport
    [转]c# System.IO.Ports SerialPort Class
    [转]用C#在windows上操控电脑自带蓝牙(入道指南)
    Springboot Actuator之四:重写与注册服务中心的健康检查逻辑(判断依据是tcp连接是否正常)
    二维码支付原理分析及安全性的探究
  • 原文地址:https://www.cnblogs.com/stoorz/p/12618622.html
Copyright © 2020-2023  润新知