zoukankan      html  css  js  c++  java
  • 数颜色

    Cici有一些彩盒,Cici喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续彩盒中,不同颜色的彩盒有多少。当然,Cici有时候会替换队列中某个彩盒的颜色。
    Input
    第一行包含两个整数N和M。
    第二行N个整数,表示初始队列中彩盒的颜色。
    接下来M行
    每行的形式为“Q L R”或“R x c”,
    “Q L R”表示Cici想知道从队列第L个彩盒到第R个彩盒中,一共有多少不同颜色的彩盒,
    “R x c”表示Cici把x位置上的彩盒换成了c颜色。
    有1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,
    小朋友Cici不会修改超过1000次,所有颜色均用1到10^6的整数表示。

    Output
    对于每个Q操作,输出一行表示询问结果。
    Sample Input
    2 3
    1 2
    Q 1 2
    R 1 2
    Q 1 2
    Sample Output
    2
    1

    Sol:

    此题主要是维护的问题,当改变一个位置的color,则影响两个color的前后关系。

    用pre[i]表示第i个元素的前一个相同元素的位置,
    问题变成在区间[l,r]里找大于l的数的个数,
    (具体什么意思去其他地方看)默认值为最大值。
    用ppp[i]表示颜色i最后出现的位置(针对这道题颜色最多1e6种,建一个1e6的数组即可)
    color[i]表示第i个位置是什么颜色

    ========================================================================

    本题要统计某个区间,出现过多少种不同的color.
    于是
    我们可以维护下某个区间[x,y]上每个点,它的color,上一次出现的位置pre[i]
    当我们走到一个点i,它的pre[i]<x,则在当前出现了一次,上一次出现位置又小于x,则说明是第一次被统计到。
    根据上面这个思路,我们可以分块处理。
    离散块暴力来找,整块操作时,可以二分来找当前区间中有多少个数字是小于x的。
    修改操作
    还要记下某个color,设为a最后出现的位置ppp[a],形成一个链表pre[a]..ppp[a]。
    当将x位置上的color,换成另一种color时
    先对删除color的点进行讨论
    1:为最后一个点
    2:不为最后一个点

    再加换上去的color进行讨论,设其最后的位置为c
    形为pre[c]............c
    分类如下
    1:在c的右边
    2:在pre[c]与c之间
    3:在pre[c]的左边形如x.......pre[c]
    此时就要找到一个位置c',形成pre[c'].....x...c'

    //https://blog.csdn.net/shisuan1/article/details/81268270
    
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    #define inf 0x3f3f3f3f
    using namespace std;
    int a[205][205];
    int pre[200005];
    int color[200005];
    int ppp[1000005];
    int n,m,l,r;
    void reset(int x)
    {
        int p=(x-1)/l; //x所在的块 
        for(int j=1;j<=l;j++) 
        {
                a[p][j]=pre[p*l+j];
        }
        sort(a[p]+1,a[p]+l+1);
    }
    void build()
    {
        for(int i=0;i<r;i++) //枚举块的个数 
        {
            for(int j=1;j<=l;j++) //枚举块大小 
            {
                a[i][j]=pre[i*l+j];
            }
            sort(a[i]+1,a[i]+l+1);
        }
    }
    int search(int x,int y)
    {
        int ans=0;
        int p=(x-1)/l+1;
        int q=(y-1)/l;
        if(p>=q)
        {
           for(int i=x;i<=y;i++) //枚举位置 
              if(pre[i]<x) 
    		  //如果这个位置的color上一次出现在x的左边,则当前出现过一次 
    		  //如果是大于x,则说明在[x,y]之间出现过不止一次 
                 ans++;
           return ans;
        }
        for(int i=x;i<=p*l;i++)
             if(pre[i]<x)
                ans++;
        for(int i=q*l+1;i<=y;i++)
             if(pre[i]<x)
                ans++;
        for(int i=p;i<q;i++)
        //对于中间的块,枚举每一块,看它里面存的数字,有多少是<x的 
        {
            ans+=lower_bound(a[i]+1,a[i]+l+1,x)-a[i]-1;
    		//后面有个减1,于是就是找<x的了 
        }
        return ans;
    }
    void change(int x,int y)//位置x,颜色y.
    {
        int p=color[x]; //记下x位置上从前的color
        color[x]=y;     //更新为当前的color
        int c=ppp[p];   //c为x这个位置上从前color的最后出现的位置
        if(c==x)        //如果这个位置等于x,即最后一个点没有了
        {
              ppp[p]=pre[c];  //则最后出现的位置变成pre[c]
        }
        else   //说明被改变color的不是其最后一个位置
        {
            while(pre[c]!=x) //不断去找这个位置
                 c=pre[c];
            pre[c]=pre[x];   //形如pre(x)......x......c,因为x这个位置去掉了,于是pre[c]=pre[x]
            reset(c);      //对c所在的段进行重构
        }
        
        c=ppp[y];  //c代表color为y的最后出现的位置
        if(c<x)    //如果这个位置小于x,形如c.....x则x是这个color的最后出现位置
        {
            pre[x]=c; 
            ppp[y]=x;
            reset(x);
        }
        else
        if(pre[c]<x)  //形如pre[c]....x......c
        {
            pre[x]=pre[c];
            pre[c]=x;
            reset(x);
            reset(c);
        }
        else  //形如x.....pre[c]
        {
            while(pre[c]>x)
            {
            c=pre[c];
            }
            //确定一个c的位置pre[c]....x....c
            pre[x]=pre[c];
            pre[c]=x;
            reset(x);
            reset(c);
        }
    }
     
    int main()
    {
        //freopen("input.txt","r",stdin);
        memset(ppp,0,sizeof(ppp));
        memset(pre,inf,sizeof(pre));
        pre[0]=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&color[i]);
            if(ppp[color[i]]!=0) //ppp[i]表示颜色i最后出现的位置
                  pre[i]=ppp[color[i]];
            else
                  pre[i]=0;   //pre[i]表示颜色i上一次出现的位置
            ppp[color[i]]=i;
        }
        l=floor(sqrt(n)); //块的大小 
        if(n%l>0)
            r=n/l+1; //块的个数 
        else
            r=n/l;
        
        build();
        int x,y;
        char c[10];
        for(int i=0;i<m;i++)
        {
            scanf("%s%d%d",c,&x,&y);
            if(c[0]=='Q')
                     printf("%d
    ",search(x,y));
            else
                     change(x,y);
        }
        return 0;
    }
    

      

     带修改的莫队算法

    https://www.cnblogs.com/RabbitHu/p/MoDuiTutorial.html

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define space putchar(' ')
    #define enter putchar('
    ')
    template <class T>
    void read(T &x){
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
            if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
            x = x * 10 + c - '0';
        if(op) x = -x;
    }
    template <class T>
    void write(T x){
        if(x < 0) x = -x, putchar('-');
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    const int N = 10005, M = 1000005, B = 464;
    int n, m, pl = 1, pr = 0, cur, res, ans[N], a[N], cnt[M];
    int idxC, idxQ, tim[N], pos[N], val[N], pre[N];
    #define bel(x) (((x) - 1) / B + 1)
    struct query 
    {
        int id, tim, l, r;
        bool operator < (const query &b) const 
    	{
            if(bel(l) != bel(b.l)) return l < b.l;
            if(bel(r) != bel(b.r)) return r < b.r;
            return id < b.id;
        }
    } q[N];
    void change_add(int cur)
    {
        if(pos[cur] >= pl && pos[cur] <= pr)
    	{
            cnt[a[pos[cur]]]--;
            if(!cnt[a[pos[cur]]]) 
    		   res--;
        }
        pre[cur] = a[pos[cur]];//记下从前的color 
        a[pos[cur]] = val[cur]; //换上新的color 
        if(pos[cur] >= pl && pos[cur] <= pr)
    	{
            if(!cnt[a[pos[cur]]]) 
    		    res++;
            cnt[a[pos[cur]]]++;
        }
    }
    void change_del(int cur)
    {
        if(pos[cur] >= pl && pos[cur] <= pr)
    	{
            cnt[a[pos[cur]]]--;
            if(!cnt[a[pos[cur]]]) res--;
        }
        a[pos[cur]] = pre[cur];
        if(pos[cur] >= pl && pos[cur] <= pr)
    	{
            if(!cnt[a[pos[cur]]]) res++;
            cnt[a[pos[cur]]]++;
        }
    }
    void change(int now)
    {
        while(cur < idxC && tim[cur + 1] <= now) 
        //cur代表目前的修改操作进行到一个了,如果还没到当前询问的时间点
    	//则有些修改操作还没有做,加进来 
    	      change_add(++cur);
        while(cur && tim[cur] > now) 
        //也有可能是多做了修改操作,于是删除这些修改操作 
    	      change_del(cur--);
    }
    void add(int p)
    {
        if(!cnt[a[p]]) 
    	    res++;
        cnt[a[p]]++;
    }
    void del(int p)
    {
        cnt[a[p]]--;
        if(!cnt[a[p]]) 
    	   res--;
    }
    bool isQ()
    {
        char op[2];
        scanf("%s", op);
        return op[0] == 'Q';
    }
    int main()
    {
        read(n), read(m);
        for(int i = 1; i <= n; i++) 
    	    read(a[i]);
        for(int i = 1; i <= m; i++)
    	{
            if(isQ())  //查询操作 
            {
    		     idxQ++;
    			 q[idxQ].id = idxQ;
    			 q[idxQ].tim = i;
    			 read(q[idxQ].l);
    			 read(q[idxQ].r);
    	    }
    		else      //修改操作 
    		     {    
    			          tim[++idxC] = i; //第idxc个修改操作发生的时间 
    					  read(pos[idxC]); //修改的位置 
    					  read(val[idxC]); //修改后的color 
    			 }  
    	}
        sort(q + 1, q + idxQ + 1);
        for(int i = 1; i <= idxQ; i++)
    	{
            change(q[i].tim); 
    		//以当前这个查询为基准,是否有修改操作没有做,或者多做了 
            while(pl > q[i].l) add(--pl);
            while(pr < q[i].r) add(++pr);
            while(pl < q[i].l) del(pl++);
            while(pr > q[i].r) del(pr--);
            ans[q[i].id] = res;
        }
        for(int i = 1; i <= idxQ; i++)
            write(ans[i]), enter;
        return 0;
    }
    

      

  • 相关阅读:
    sqlnet设置网络传输加密
    临时表处理办法
    分布式事务2PC_PENDING异常处理
    统计信息收集百分比和并行改变
    dataguard丢失归档日志处理
    patch 28729262
    Cardinality
    统计信息不准导致sql性能下降
    子查询展开
    ogg 单表拆分合并进程
  • 原文地址:https://www.cnblogs.com/cutemush/p/12800928.html
Copyright © 2011-2022 走看看