• 洛谷 P4108 / loj 2119 [HEOI2015] 公约数数列 题解【分块】


    看样子分块题应该做的还不够。

    题目描述

    设计一个数据结构. 给定一个正整数数列 (a_0, a_1, ldots , a_{n-1}),你需要支持以下两种操作:

    1. MODIFY id x: 将 (a_{mathrm{id}}) 修改为 (x).
    2. QUERY x: 求最小的整数 (p(0 leq p < n)),使得 (gcd(a_0, a_1, ..., a_p) cdot operatorname{XOR}(a_0, a_1, ..., a_p) = x). 其中 (operatorname{XOR}(a_0, a_1, ..., a_p)) 代表 (a_0, a_1, ldots , a_p) 的异或和,(gcd) 表示最大公约数。

    输入输出格式

    输入格式:

    输入数据的第一行包含一个正整数 (n)

    接下来一行包含 (n) 个正整数 (a_0, a_1, ..., a_{n-1})

    之后一行包含一个正整数 (q),表示询问的个数。

    之后 (q) 行,每行包含一个询问。格式如题目中所述。

    输出格式:

    对于每个 QUERY 询问,在单独的一行中输出结果。如果不存在这样的 (p),输出 no.

    输入输出样例

    样例输入:

    10
    1353600 5821200 10752000 1670400 3729600 6844320 12544000 117600 59400 640
    10
    MODIFY 7 20321280
    QUERY 162343680
    QUERY 1832232960000
    MODIFY 0 92160
    QUERY 1234567
    QUERY 3989856000
    QUERY 833018560
    MODIFY 3 8600
    MODIFY 5 5306112
    QUERY 148900352
    

    输出样例:

    6
    0
    no
    2
    8
    8
    

    数据范围与约定

    对于 (100\%) 的数据,(n leq100000, q leq 10000, a_i leq 10^9 (0 leq i < n))

    QUERY x 中的 (x leq 10^{18})

    MODIFY id x 中的 (0 leq mathrm{id} < n, 1 leq x leq 10^9)

    题解:

    感觉需要推导的东西还是比较多的。但是以后要记着看到奇怪的(?)数据范围时要想到分块。

    这个题有一定的特点,就是两个表达式都是前缀形式。对于 (gcd​) 而言,它一定是递减的,而且不同的数值最多只有 (O(log a_i)​) 个,因为每次至少会变为原来的 (frac 12​)。因此产生变化的位置是可以考虑枚举的。

    注意到询问是 (10000​) 的,不是很大。我们对原区间进行 (O(sqrt n)​) 分块。

    因为要找的是最小的,所以应该从前往后枚举。对于 (gcd) 整块不变的那些块,我们只需要找到其中是否存在异或前缀和恰好为 (x/gcd) 的。这一询问可以用 std::map 来解决。

    当涉及单点修改时,我们只需要对当前块中的 map 进行修改就可以了。

    实际上 map 中存的是当前块中的异或前缀和,由于我们每次查询都是从前往后查,可以积累一定的信息。因此不必也不能维护全局前缀和,不然修改时影响的范围太大。

    不过分块的细节挺多的,包括边界一类的。还要注意复杂度,不能把 gcd() 函数当 (O(1)) 的操作用啊啊啊啊啊啊…= =

    时间复杂度 (O(nlog a_i+qsqrt nlog a_i))

    Code:

    #include<cstdio>
    #include<cstring>
    #include<map>
    #include<cmath>
    #define inf 2147438647
    using std::map;
    int gcd(int x,int y)
    {
        while(y)
        {
            int t=y;
            y=x%y;
            x=t;
        }
        return x;
    }
    map<int,int> f[400];
    int a[400][400],g[400][400];
    int G[400],sum[400];
    int main()
    {
        int n,m,u;
        scanf("%d",&n);
        int b=sqrt(n),tmp=0;
        for(int i=1;i<=n;++i)
        {
            int x=(i-1)/b,y=(i-1)%b+1;
            scanf("%d",&a[x][y]);
            tmp^=a[x][y];
            g[x][y]=gcd(a[x][y],g[x][y-1]);
            G[x]=g[x][y];
            if(f[x].find(tmp)==f[x].end())
                f[x][tmp]=y;
            if(x!=i/b)
            {
                sum[x]=tmp;
                g[x+1][0]=G[x];
                tmp=0;
            }
        }
        scanf("%d",&m);
        char op[100];
        int tot=ceil((double)n/b)-1;
        while(m--)
        {
            scanf("%s",op);
            if(op[0]=='M')
            {
                scanf("%d",&u);
                ++u;
                int x=(u-1)/b,y=(u-1)%b+1;
                scanf("%d",&a[x][y]);
                f[x].clear();
                tmp=0;
                g[x][0]=x?G[x-1]:0;
                for(int j=1;j<=b;++j)
                {
                    tmp^=a[x][j];
                    if(f[x].find(tmp)==f[x].end())
                        f[x][tmp]=j;
                    g[x][j]=gcd(a[x][j],g[x][j-1]);
                    G[x]=g[x][j];
                }
                sum[x]=tmp;
            }
            else
            {
                long long k;
                scanf("%lld",&k);
                int flag=0;
                tmp=0;
                int gtmp=a[0][1];
                for(int i=0;i<=tot;++i)
                {
                    if(!i||G[i]!=G[i-1])//暴力
                    {
                        for(int j=1;j<=b;++j)
                        {
                            tmp^=a[i][j];
                            gtmp=g[i][j];
                            if((long long)tmp*gtmp==k)
                            {
                                flag=1;
                                printf("%d
    ",i*b+j-1);
                                break;
                            }
                        }
                        if(flag)
                            break;
                    }
                    else
                    {
                        gtmp=G[i];
                        if(k%gtmp==0&&k/gtmp<inf&&f[i].find(k/gtmp^tmp)!=f[i].end())
                        {
                            printf("%d
    ",i*b+f[i][k/gtmp^tmp]-1);
                            flag=1;
                            break;
                        }
                        tmp^=sum[i];
                    }
                }
                if(flag)
                    continue;
                puts("no");
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Contiki学习笔记  第一个程序:Hello World
    contiki学习笔记---process结构体
    MYSQL碰到The total number of locks exceeds the lock table size 问题解决记录
    navicat连接mysql查询结果中文都是?号(C#)
    C#使用OracleBulkCopy
    解决IIS应用程序池默认回收导致程序崩溃
    构建可读性更高的 ASP.NET Core 路由机制
    .Net(C#)汉字和Unicode编码互相转换
    聊聊c#字符串拼接
    VS2010到VS2019各个版本的密钥
  • 原文地址:https://www.cnblogs.com/wjyyy/p/lg4108.html
Copyright © 2020-2023  润新知