zoukankan      html  css  js  c++  java
  • Lydsy2120 数颜色(分块写法)

    题目

    Description

    墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。
    墨墨会像你发布如下指令: 
    1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 
    2、 R P Col 把第P支画笔替换为颜色Col。
    为了满足墨墨的要求,你知道你需要干什么了吗?

    Input

    第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。
    第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。
    第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
    N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6

    Output

    对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

    Sample Input

    6 5
    1 2 3 4 5 5
    Q 1 4
    Q 2 6
    R 1 2
    Q 1 4
    Q 2 6
    

    Sample Output

    4
    4
    3
    4



    思路:

    对于这一题,可以用分块,也可以用带修改的莫队;
    我先讲下分块写法
    可以用一个pre数字存每一个颜色上一次出现的位置;
    如果这个pre位置是小于询问区间的左端点的,那么代表这个颜色是在这个询问区间内第一次出现的;
    这样就可以查找这个区间内有多少个不同的颜色;
    然后,我们可以定义一个last数组,存这个颜色的最后出现位置;
    同时last数组可以方便我们更好的记录pre;
    然后是修改操作;
    可以重新再把每一个颜色的pre重新构建;
     
    inline void findout(ll x,ll y)
    {
        for(ll i=1;i<=n;i++)
            last[color[i]]=0;
        color[x]=y;
        for(ll i=1;i<=n;i++)
        {
            ll num=pre[i];
            pre[i]=last[color[i]];
            last[color[i]]=i;
            if(pre[i]!=num)
                dowork(i);
        }
    }
    也可以找到与被修改的颜色相关的点,然后用if修改关于这个颜色的pre,last数组;
    这样修改将比上面的代码更快;
    代码,最后会给出;

    最后,总结下思路,
    分块,排序后二分找整块,暴力枚举小棱块,查找pre
    位置小于询问区间的左端的那个颜色

    附上熬夜修改的代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline ll read()
    {
        ll a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    ll n,m,blo;
    ll a[1000001],color[1000001],sv[1000001];//a数组是每个位置所处的分块,
                                             //sv是按每个分块排序后的pre值 
    ll pre[1000001],last[1000001];
    inline ll L(ll x)//懒人专用
                     //求每个分块左右的端点 
    {
        return (x-1)*blo+1;
    }
    inline ll R(ll x)
    {
        return x*blo;
    }
    inline void dowork(ll x)
    {
        for(ll i=L(a[x]);i<=R(a[x]);i++)
            sv[i]=pre[i];
        sort(sv+L(a[x]),sv+R(a[x])+1);//pre修改后,重新排序构建,便于二分查找 
    }
    inline void findout(ll x,ll y)//x表示被修改的位置,y是要变成的颜色 
    {
        ll cox=color[x];//记录这个颜色 
        color[x]=y;    //修改 
        ll now=last[cox];//记录这个要被修改的颜色的最后一个点 
        if(now==x)//如果最后一个点要被修改 
            last[cox]=pre[x];//那么这个颜色的最后一个点,将是被修改的颜色的pre 
        else if(now>x)//若  x.........now  不会出现x<now的情况,因为now=last[cox]; 
        {
            while(pre[now]!=x)//不断往前找,使得pre[now]==x 
                now=pre[now];
            pre[now]=pre[x];// pre[x]......x......now  因为x会被修改,所以 pre[now]=pre[x]
            dowork(now);//因为pre被修改了,所以now所在的块重新排序 
        }
        now=last[y];//记下y颜色的最后一个位置 
        if(now<x)//因为 y!=x 所以 不考虑now==x的情况 
        {
            last[y]=x;// now......x  y颜色的最后位置将会变成x 
            pre[x]=now;
            dowork(x);//重新排序 
        }
        else if(now>x)//x......now
        {
            while(pre[now]>x)//不断找到,使得pre[now]<x 
                now=pre[now];
            pre[x]=pre[now];//pre[now]....x.....now
            pre[now]=x;
            dowork(x);
            dowork(now);
        }
    }
    //inline void findout(ll x,ll y) //重新构建方法,比上面的更慢 
    //{
    //    for(ll i=1;i<=n;i++)
    //        last[color[i]]=0;  //所以颜色的最后一个位置清零 
    //    color[x]=y;
    //    for(ll i=1;i<=n;i++)//重新枚举构建 
    //    {
    //        ll num=pre[i];
    //        pre[i]=last[color[i]];
    //        last[color[i]]=i;
    //        if(pre[i]!=num)//如果重新构建之前 与构建之后的pre值改变了 
    //            dowork(i);//就整块分块内 重新排序 
    //    }
    //}
    inline ll reallyans(ll x,ll y)
    {
        ll sum=0;
        if(a[x]==a[y])//如果询问区间处在一个分块,就暴力 
        {
            for(ll i=x;i<=y;i++)
            if(pre[i]<x) //查找pre位置小于询问区间的左端的那个颜色
                sum++;
            return sum;
        }
        else
        {
            for(ll i=a[x]+1;i<=a[y]-1;i++)
            {
                ll l=L(i),r=R(i),ss=0,mid;
                while(l<=r)
                {
                    mid=(l+r)>>1;
                    if(sv[mid]<x)
                        l=mid+1,
                        ss=mid-L(i)+1;//如果中间的那个sv的位置都小于x
                                     //因为是排好序的,所以左边的位置都小于x 
                    else
                        r=mid-1;
                }
                sum+=ss;
            }//二分找位置小于询问区间的左端的那个颜色
            for(ll i=x;i<=R(a[x]);i++)//暴模边角 
            if(pre[i]<x)
                sum++;
            for(ll i=L(a[y]);i<=y;i++)
            if(pre[i]<x)
                sum++;
            return sum;
        }
    }
    int main()
    {
        n=read();m=read();
        blo=sqrt(n);
        for(ll i=1;i<=n;i++)
        {
            color[i]=read();
            a[i]=(i-1)/blo+1;
            pre[i]=last[color[i]];
            last[color[i]]=i;
        }//记录pre和last的值 
        for(ll i=1;i<=n;i++)
            sv[i]=pre[i];
        for(ll i=1;i<=a[n];i++)
            sort(sv+L(i),sv+R(i)+1);//每个分块内都排好序 
        char cc[4];
        ll ans;
        for(ll i=1;i<=m;i++)
        {
            scanf("%s",cc);
            ll x=read(),y=read();
            if(cc[0]=='R')
                findout(x,y);
            else if(cc[0]=='Q')
            {
                ans=reallyans(x,y);
                printf("%lld
    ",ans);
            }
        }
        return 0;//别忘了return 0 ^_^ 
    }
     
  • 相关阅读:
    目标检测算法的进展
    基础 | batchnorm原理及代码详解
    MTCNN人脸及特征点检测---代码应用详解(基于ncnn架构)
    Android.mk文件c++头文件包含问题
    linux下的find文件查找命令与grep文件内容查找命令
    TensorFlow基础笔记(15) 编译TensorFlow.so,提供给C++平台调用
    TensorFlow基础笔记(0) tensorflow的基本数据类型操作
    tensorflow函数解析:Session.run和Tensor.eval的区别
    TensorFlow基础笔记(14) 网络模型的保存与恢复_mnist数据实例
    SQL Server 2005/2008遍历所有表更新统计信息
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/12940917.html
Copyright © 2011-2022 走看看