BZOJ 4026 dC Loves Number Theory 分块+十字链表/可持久化线段树

题目大意:给定一个序列,多次询问某段区间乘积的φ值对1000777的模

我竟然卡过去了233333

将序列分块,记录fi,j表示第i块左端点到第j个点中出现的所有质数p的p?1p之积

每次询问[x,y],首先取出[x,y]区间内所有数的积,然后乘上fst,y(其中st是x后面第一个块端点所在块)

现在还剩[x,l[st]]部分没有统计

由于106以内的数分解得到的不同的质因数最多只有7个,因此暴力枚举零碎部分的质数即可

现在对于每个质数我们需要判断是否出现过

我们只需要判断这个质数下一次出现的位置是否大于y即可

用十字链表搞一搞就可以了

时间复杂度O(7mn√)

这当然不是正解- -

珍爱生命,远离卡常,我们来看正解吧

实际上我们要统计的就是[x,y]区间内所有出现过的质数p的p?1p之积

我们想到了什么?没错,HH的项链!

由于强制在线,因此我们用可持久化线段树搞一搞就行了。时间复杂度O(mlogn)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 50500
#define B 700
#define MOD 1000777
using namespace std;
struct abcd{
    abcd *up,*rt;
    int p,belong;
    void* operator new (size_t)
    {
        static abcd mempool[M*7],*C=mempool;
        return C++;
    }
}*head[M],*last[80800];
int n,m,b,last_ans;
int a[M];
int prime[80800],tot;
long long inv[MOD];
int l[M],belong[M];
int prod[B][M];
void Linear_Shaker()
{
    static bool not_prime[1001001];
    int i,j;
    for(i=2;i<=1000000;i++)
    {
        if(!not_prime[i])
            prime[++tot]=i;
        for(j=1;prime[j]*i<=1000000;j++)
        {
            not_prime[prime[j]*i]=true;
            if(i%prime[j]==0)
                break;
        }
    }
    for(inv[1]=1,i=2;i<MOD;i++)
        inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
}
abcd* Decomposition(int pos)
{
    int i,n=a[pos];
    abcd *re=0x0;
    for(i=1;prime[i]*prime[i]<=n;i++)
        if(n%prime[i]==0)
        {
            abcd *temp=new abcd;
            temp->up=re;
            temp->p=i;
            temp->belong=pos;
            if(last[i])
                last[i]->rt=temp;
            re=last[i]=temp;

            while(n%prime[i]==0)
                n/=prime[i];
        }
    if(n!=1)
    {
        i=lower_bound(prime+1,prime+tot+1,n)-prime;

        abcd *temp=new abcd;
        temp->up=re;
        temp->p=i;
        temp->belong=pos;
        if(last[i])
            last[i]->rt=temp;
        re=last[i]=temp;
    }
    return re;
}
int Query(int x,int y)
{
    int i,st=belong[x-1]+1,re=inv[a[x-1]]*a[y]%MOD;
    abcd *temp;
    if(y>=l[st])
        re=(long long)re*prod[st][y]%MOD;
    for(i=min(y,l[st]-1);i>=x;i--)
        for(temp=head[i];temp;temp=temp->up)
            if(!temp->rt||temp->rt->belong>y)
                re=re*inv[prime[temp->p]]%MOD*(prime[temp->p]-1)%MOD;
    return re;
}
namespace IStream{
    const int L=1<<15;
    char buffer[L],*S,*T;
    inline char Get_Char()
    {
        if(S==T)
        {
            T=(S=buffer)+fread(buffer,1,L,stdin);
            if(S==T) return EOF;
        }
        return *S++;
    }
    inline int Get_Int()
    {
        char c;
        int re=0;
        for(c=Get_Char();c<‘0‘||c>‘9‘;c=Get_Char());
        while(c>=‘0‘&&c<=‘9‘)
            re=(re<<1)+(re<<3)+(c-‘0‘),c=Get_Char();
        return re;
    }
}
int main()
{
    using namespace IStream;
    int i,j,x,y;
    abcd *temp;
    cin>>n>>m;
    b=200;
    Linear_Shaker();
    for(i=1;i<=n;i++)
    {
        a[i]=Get_Int();
        head[i]=Decomposition(i);
    }
    for(a[0]=1,i=1;i<=n;i++)
        a[i]=(long long)a[i]*a[i-1]%MOD;
    for(i=1;i<=n;i++)
        belong[i]=(i-1)/b+1;
    for(i=1;i<=belong[n];i++)
        l[i]=(i-1)*b+1;
    l[i]=0x3f3f3f3f;

    static int v[80800],ans;
    for(j=1;j<=belong[n];j++)
    {
        ans=1;
        for(i=l[j];i<=n;i++)
        {
            for(temp=head[i];temp;temp=temp->up)
                if(v[temp->p]!=j)
                {
                    v[temp->p]=j;
                    ans = ans * inv[prime[temp->p]] % MOD * (prime[temp->p]-1) % MOD ;
                }
            prod[j][i]=ans;
        }
    }

    for(i=1;i<=m;i++)
    {
        x=Get_Int();
        y=Get_Int();
        x^=last_ans;
        y^=last_ans;
        printf("%d\n",last_ans=Query(x,y));
    }
    //puts("Fuck♂You!");
    return 0;
}
时间: 2024-08-03 12:09:12

BZOJ 4026 dC Loves Number Theory 分块+十字链表/可持久化线段树的相关文章

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

题目大意:给你一个序列,求出指定区间的(l<=i<=r) mod 1000777 的值 还复习了欧拉函数以及线性筛逆元 考虑欧拉函数的的性质,(l<=i<=r),等价于 (p[j]是区间内所有出现过的质数) 那么考虑找出区间内所有出现过的质数,这思路和HH的项链是不是很像?? 由于此题强制在线,所以把树状数组替换成了主席树而已 原来我以前写的主席树一直都是错的......还好推出了我原来错误代码的反例 在继承上一个树的信息时,注意不要破坏现在的树 1 #include <cs

【bzoj4026】dC Loves Number Theory 可持久化线段树

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

[BZOJ4026]dC Loves Number Theory

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

POJ-2104-K-th Number(可持久化线段树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 55456   Accepted: 19068 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

BZOJ 1901: Zju2112 Dynamic Rankings 区间k大 带修改 在线 线段树套平衡树

之前写线段树套splay数组版..写了6.2k..然后弃疗了.现在发现还是很水的..嘎嘎.. zju过不了,超时. upd:才发现zju是多组数据..TLE一版才发现.然后改了,MLE...手写内存池..尼玛终于过了..附zju2112代码于后. bzoj倒是过了,1A的感觉还是很爽的..可是时间不好看..这就是所谓\(O(nlog^3n)\)的复杂度的可怜之处么? 写挂的地方: insert一定要是传地址指针进去. delete时先把地址指针delete掉,最后把是地址指针指向左儿子or右儿子

[BZOJ 3207] 花神的嘲讽计划Ⅰ【Hash + 可持久化线段树】

题目链接:BZOJ - 3207 题目分析 先使用Hash,把每个长度为 k 的序列转为一个整数,然后题目就转化为了询问某个区间内有没有整数 x . 这一步可以使用可持久化线段树来做,虽然感觉可以有更简单的做法,但是我没有什么想法... 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #inclu

[BZOJ 3218] A + B Problem 【可持久化线段树 + 网络流】

题目连接:BZOJ - 3218 题目分析 题目要求将 n 个点染成黑色或白色,那么我们可以转化为一个最小割模型. 我们规定一个点 i 最后属于 S 集表示染成黑色,属于 T 集表示染成白色,那么对于每个点 i 就要连边 (S, i, B[i]) 和 (i, T, W[i]). 这样,如果一个点属于 S 集,就要割掉与 T 相连的边,就相当于失去了染成白色的收益. 我们再来考虑 “奇怪的点”,一个点 i 变成奇怪的点的条件是:i 是黑色且存在一个白色点 j 满足 j < i && L

BZOJ 1112 [POI2008]砖块Klo(可持久化线段树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1112 [题目大意] 给出一个数列,对于一个操作,你可以对一个数+1,或者一个数-1, 问若使得数列中出现长度为m的连续相同的数,最少需要的操作数. [题解] 我们发现对于固定区间求最小操作,等价于求区间中数距离中位数差值和最小, 我们发现区间中位数可以利用主席树求区间kth来实现, 同时在主席树上维护权值线段树的区间真值和,那么对于每个区间中的数, 就能分别维护比中位数小的部分的和以

【BZOJ 3674】可持久化并查集加强版&amp;【BZOJ 3673】可持久化并查集 by zky 用可持久化线段树破之

最后还是去掉异或顺手A了3673,,, 并查集其实就是fa数组,我们只需要维护这个fa数组,用可持久化线段树就行啦 1:判断是否属于同一集合,我加了路径压缩. 2:直接把跟的值指向root[k]的值破之. 3:输出判断即可. 难者不会,会者不难,1h前我还在膜这道题,现在吗hhh就当支持下zky学长出的题了. 3673: #include<cstdio> #include<cstring> #include<algorithm> #define read(x) x=ge