bzoj 4026 dC Loves Number Theory (主席树+数论+欧拉函数)

本文介绍了一个算法问题的解决方法,利用主席树和线性筛逆元技巧求解特定区间的值。通过欧拉函数性质转换问题,实现在线查询,并详细展示了代码实现过程。

题目大意:给你一个序列,求出指定区间的\varphi (a[i])(l<=i<=r) mod 1000777 的值

还复习了欧拉函数以及线性筛逆元

考虑欧拉函数的的性质,\varphi (\prod a[i])(l<=i<=r),等价于\prod a[i]*\prod p[j]-1/p[j] (p[j]是区间内所有出现过的质数)

那么考虑找出区间内所有出现过的质数,这思路和HH的项链是不是很像??

由于此题强制在线,所以把树状数组替换成了主席树而已

原来我以前写的主席树一直都是错的......还好推出了我原来错误代码的反例

在继承上一个树的信息时,注意不要破坏现在的树

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long 
#define il inline
#define N 50010
#define maxn 1000000
#define mod 1000777
using namespace std;
 
int n,q,ctp,tot;
int root[N];
int pr[maxn+100],use[maxn+100],lst[maxn+100];
ll a[N],inv[mod+100],nxt[maxn+100];
struct Seg{ll sum;int ls,rs;}seg[N*60]; //re
void prime_inv()
{
    for(int i=2;i<=maxn;i++)
    {
        if(!use[i])
            pr[++ctp]=i,nxt[i]=i;
        for(int j=1;j<=ctp&&i*pr[j]<=maxn;j++){
            use[i*pr[j]]=1,nxt[i*pr[j]]=pr[j];
            if(i%pr[j]==0) break;
        }
    }
    inv[0]=inv[1]=1;
    for(ll i=2;i<mod;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
ll gc()
{
    ll ret=0,fh=1;char p=getchar();
    while(p<'0'||p>'9') {if(p=='-')fh=-1;p=getchar();}
    while(p>='0'&&p<='9') {ret=(ret<<3)+(ret<<1)+p-'0';p=getchar();}
    return ret*fh;
}
il void pushup(int rt){seg[rt].sum=(seg[seg[rt].ls].sum*seg[seg[rt].rs].sum)%mod;}
void build(int l,int r,int rt)
{
    seg[rt].sum=1;
    if(l==r)return;
    int mid=(l+r)>>1;
    seg[rt].ls=++tot,build(l,mid,tot);
    seg[rt].rs=++tot,build(mid+1,r,tot);
}
void update(int x,int l,int r,int rt1,int rt2,ll w)
{
    if(l==r) {seg[rt2].sum=(seg[rt2].sum*w)%mod;return;}
    int mid=(l+r)>>1;
    if(x<=mid)
    {
        if(!seg[rt2].ls||seg[rt1].ls==seg[rt2].ls){
            seg[rt2].ls=++tot,seg[seg[rt2].ls].sum=seg[seg[rt1].ls].sum;
        if(!seg[rt2].rs) 
            seg[rt2].rs=seg[rt1].rs;
        } 
        update(x,l,mid,seg[rt1].ls,seg[rt2].ls,w);
    }else{
        if(!seg[rt2].rs||seg[rt1].rs==seg[rt2].rs){
            seg[rt2].rs=++tot,seg[seg[rt2].rs].sum=seg[seg[rt1].rs].sum;
        if(!seg[rt2].ls)
            seg[rt2].ls=seg[rt1].ls;
        }
        update(x,mid+1,r,seg[rt1].rs,seg[rt2].rs,w);
    }
    pushup(rt2);
}
ll query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R) return seg[rt].sum;
    int mid=(l+r)>>1;ll ans=1;
    if(L<=mid) ans*=query(L,R,l,mid,seg[rt].ls),ans%=mod;
    if(R>mid) ans*=query(L,R,mid+1,r,seg[rt].rs),ans%=mod;
    return ans;
}
 
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) a[i]=gc();
    prime_inv();
    root[0]=++tot;
    build(1,n,1);
    ll x,p,w;
    for(int i=1;i<=n;i++)
    {
        x=a[i],w=a[i],root[i]=++tot;
        while(x!=1){
            p=nxt[x];
            if(lst[p])
                update(lst[p],1,n,root[i-1],root[i],(inv[p-1]*p)%mod);
            lst[p]=i;
            x/=p,w=((w*(p-(ll)1)%mod)*inv[p])%mod;
            while(x%p==0) x/=p;
        }
        update(i,1,n,root[i-1],root[i],w);
    }
    ll l,r,ans=0;
    for(int i=1;i<=q;i++)
    {
        l=gc(),r=gc();
        l^=ans,r^=ans;
        ans=query(l,r,1,n,root[r]);
        printf("%lld\n",ans);
    }
    return 0;
}

 

题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值