• bzoj 4373 算术天才⑨与等差数列——线段树+set


    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4373

    能形成公差为k的等差数列的条件:mx-mn=k*(r-l) && 差分数组gcd为k  && 区间内没有重复的数。

    这些都可以线段树维护。

    那个“没有重复的数”需要给每个位置记下权值的pre,修改的时候需要找到它前面一个和后面一个。

    用链表的话没法插入。可以给每个权值开一个set,就能用lower_bound了。

    有些细节。重载运算符很方便。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    const int N=3e5+5,INF=1e9+5;
    int n,m,c[N],ls[N<<1],rs[N<<1],tot,pre[N],lt,cnt;
    map<int,int> mp;
    set<int> s[N<<1];
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int abss(int k){return k<0?-k:k;}
    struct Node{
      int mx,mn,lst,g;
      Node(int a=0,int b=0,int l=0,int g=0):mx(a),mn(b),lst(l),g(g) {}
      Node operator+ (const Node &b)const
      {
        return Node(max(mx,b.mx),min(mn,b.mn),max(lst,b.lst),gcd(g,b.g));
      }
    }a[N<<1];
    void add(int x)
    {
      mp[x]=++cnt;s[cnt].insert(0);s[cnt].insert(n+1);
    }
    void pshp(int cr)
    {
      a[cr]=a[ls[cr]]+a[rs[cr]];
    }
    void build(int l,int r,int cr)
    {
      if(l==r)
        {
          a[cr]=Node(c[l],c[l],pre[l],abss(c[l+1]-c[l]));return;
        }
      int mid=((l+r)>>1);
      ls[cr]=++tot;build(l,mid,ls[cr]);
      rs[cr]=++tot;build(mid+1,r,rs[cr]);
      pshp(cr);//!!!
    }
    void updt(int l,int r,int cr,int p)
    {
      if(l==r)
        {
          a[cr]=Node(c[l],c[l],pre[l],abss(c[l+1]-c[l]));return;
        }
      int mid=((l+r)>>1);
      if(p<=mid)updt(l,mid,ls[cr],p);
      else updt(mid+1,r,rs[cr],p);
      pshp(cr);
    }
    Node query(int l,int r,int cr,int L,int R)
    {
      if(l>=L&&r<=R)return a[cr];
      int mid=((l+r)>>1);
      if(L>mid)return query(mid+1,r,rs[cr],L,R);
      if(R<=mid)return query(l,mid,ls[cr],L,R);
      return query(l,mid,ls[cr],L,R)+query(mid+1,r,rs[cr],L,R);
    }
    int main()
    {
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)
        {
          scanf("%d",&c[i]);
          if(!mp[c[i]])add(c[i]);
          int t=mp[c[i]];
          pre[i]=*--s[t].lower_bound(i);
          s[t].insert(i);
        }
      a[n+1]=a[n];//?是为了让最后一个点的差分是0,使不影响吧 
      tot=1;build(1,n,1);
      int op,x,y;
      while(m--)
        {
          scanf("%d",&op);
          if(op==1)
        {
          scanf("%d%d",&x,&y);x^=lt;y^=lt;
          int t=mp[c[x]];
          int u=*--s[t].lower_bound(x),v=*s[t].upper_bound(x);
          if(v!=n+1)pre[v]=u,updt(1,n,1,v);//v!=n+1
          s[t].erase(x);
          if(!mp[y])add(y);t=mp[y];
          u=*--s[t].lower_bound(x);v=*s[t].upper_bound(x);
          if(v!=n+1)pre[v]=x,updt(1,n,1,v);//v!=n+1
          pre[x]=u;c[x]=y;s[t].insert(x);
          updt(1,n,1,x);
          if(x!=1)updt(1,n,1,x-1);//x!=1//为了改差分 
        }
          else
        {
          int L,R,k;
          scanf("%d%d%d",&L,&R,&k);L^=lt;R^=lt;k^=lt;
          if(L==R){printf("Yes
    ");lt++;continue;}//放在下面那个if的上面! 
          Node ans0=query(1,n,1,L,R-1),ans=ans0+query(1,n,1,R,R);//为了差分弄ans0 
          if(!k)    //放在下面那个if的上面! 
            {
              if(ans.mx==ans.mn)printf("Yes
    "),lt++;
              else printf("No
    ");
              continue;
            }
          if(ans.lst<L&&ans.mx-ans.mn==k*(R-L)&&ans0.g==k)printf("Yes
    "),lt++;
          else printf("No
    ");
        }
        }
      return 0;
    }
  • 相关阅读:
    扩展GridView控件增加选择列
    Flash图片轮换/切换左右推拉效果
    Flash图片轮换/切换普通效果1
    WebForm_PostBackOptions未定义 解决方法
    SQL Server 2008无法还原日志备份或差异备份的问题解决(转)
    一个简单的C#多线程间同步的例子.[转]
    SQL2008报错:无法还原日志备份或差异备份,因为没有文件可用于前滚(转)
    C# 字符、字符串过滤,只能输入数字、中文、英文、大写、小写(转)
    C# Monitor类锁定对象
    使用SqlServer中的float类型时发现的问题(转)
  • 原文地址:https://www.cnblogs.com/Narh/p/9300742.html
Copyright © 2020-2023  润新知