区间相关值问题

21 篇文章 0 订阅
13 篇文章 0 订阅

这是一篇奇怪向的总结==
区间max,区间和。。。线段树直接搞
区间中位数,树套树或许可以但或许不优。。。
区间mex(无修改):首先 [1,x],1xn 单调扫一遍即可,
同时处理出该位置上的数下一次出现位置,记为 next[i]
然后我们对询问排序,考虑 i 上数对后面数的影响,即为[i,next[i]1] mex>=num[i] 变为 num[i] ,套上线段树区间修改即可。(ps:带修改或许可以用带修莫队)code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x7fffffff
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
int n,m,ans;
struct hp{
    int mex,delta;
}seg[800001];
int ansi[200001];
int next[200001],last[200001];
struct hq{
    int l,r,num;
    bool operator < (const hq &a) const
      {return (l<a.l)||(l==a.l&&r<a.r);}
}qst[200001];
bool mark[200001];
int st[200001],xl[200001];
void build(int i,int l,int r)
{
    seg[i].delta=inf;
    if (l==r)
      {
        seg[i].mex=st[l];
        return;
      }
    build(lch); build(rch);
}
void pushdown(int i)
{
    int a=seg[i].delta;
    seg[i<<1].delta=min(seg[i<<1].delta,a);
    seg[i<<1|1].delta=min(seg[i<<1|1].delta,a);
    seg[i<<1].mex=min(seg[i<<1].mex,a);
    seg[i<<1|1].mex=min(seg[i<<1|1].mex,a);
    seg[i].delta=inf;
}
void insert(int i,int l,int r,int x,int y,int a)
{
    if (x<=l&&y>=r)
      {
        seg[i].mex=min(seg[i].mex,a);
        seg[i].delta=min(seg[i].delta,a);
        return;
      }
    if (seg[i].delta!=inf)
      pushdown(i);
    if (x<=mid) insert(lch,x,y,a);
    if (y>mid) insert(rch,x,y,a);
}
void query(int i,int l,int r,int x)
{
    if (l==x&&l==r)
      {
        ans=seg[i].mex;
        return;
      }
    if (seg[i].delta!=inf)
      pushdown(i);
    if (x<=mid) query(lch,x); if (x>mid) query(rch,x);
}
int main()
{
    int i,k=0,now;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
      scanf("%d",&xl[i]);
    for (i=1;i<=n;++i)
      {
        mark[xl[i]]=1;
        if (xl[i]==k) 
          while (mark[k])
            k++;
        st[i]=k;
      }
    build(1,1,n);
    for (i=n;i>=1;--i)
      {
        next[i]=last[xl[i]];
        if (last[xl[i]]==0) next[i]=n+1;
        last[xl[i]]=i;
      }
    for (i=1;i<=m;++i)
      {
        scanf("%d%d",&qst[i].l,&qst[i].r);
        qst[i].num=i;
      }
    sort(qst+1,qst+m+1);
    now=1;
    for (i=1;i<=m;++i)
      {
        while (now<qst[i].l)
          {
            insert(1,1,n,now,next[now]-1,xl[now]);
            now++;
          }
        query(1,1,n,qst[i].r);
        ansi[qst[i].num]=ans;
      }
    for (i=1;i<=m;++i)
      printf("%d\n",ansi[i]);
}

区间众数(无修改),分块做法,比较简单,参考一下代码吧
g[i][j] 表示从第 i 个块开始到第j个结束的众数
f[x][i] 表示 x 在前i个块内的出现次数
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int f[40001][201],g[201][201];
int fa[40001];
int a[40001],b[40001],cnt[40001];
int block[201],num[40001];
int n,m,t,lastans;
void work(int l,int r)
{
    int i,st,en,ans,maxn;
    st=lower_bound(block+1,block+t+1,l)-block;
    en=lower_bound(block+1,block+t+1,r)-block;
    if (en-st<=1)
      {
        for (i=l;i<=r;++i)
          num[a[i]]++;
        ans=0; maxn=0;
        for (i=l;i<=r;++i)
          {
            if ((num[a[i]]>maxn)||(num[a[i]]==maxn&&a[i]<ans))
              {
                maxn=num[a[i]];
                ans=a[i];
              }
            num[a[i]]=0;
          }
      }
    else
      {
        ans=g[st+1][en-1]; maxn=f[ans][en-1]-f[ans][st]; 
        for (i=l;i<=block[st];++i)
          num[a[i]]++;
        for (i=block[en-1]+1;i<=r;++i)
          num[a[i]]++;
        for (i=l;i<=block[st];++i)
          {
            if ((num[a[i]]+f[a[i]][en-1]-f[a[i]][st]>maxn)||(num[a[i]]+f[a[i]][en-1]-f[a[i]][st]==maxn&&ans>a[i]))
              {
                maxn=f[a[i]][en-1]-f[a[i]][st]+num[a[i]];
                ans=a[i];
              }    
            num[a[i]]=0;
          }
        for (i=block[en-1]+1;i<=r;++i)
          {
            if ((num[a[i]]+f[a[i]][en-1]-f[a[i]][st]>maxn)||(num[a[i]]+f[a[i]][en-1]-f[a[i]][st]==maxn&&ans>a[i]))
              {
                maxn=f[a[i]][en-1]-f[a[i]][st]+num[a[i]];
                ans=a[i];
              }
            num[a[i]]=0;
          }
      }
    printf("%d\n",fa[ans]);
    lastans=fa[ans];
}
int main()
{
    int i,per,k,j,size,l,r;
    int ans,maxn;
    scanf("%d%d",&n,&m); per=sqrt(n);
    for (i=1;i<=n;++i)
      {
        scanf("%d",&a[i]);
        b[i]=a[i];
      }
    sort(b+1,b+n+1);
    size=unique(b+1,b+n+1)-b-1;
    for (i=1;i<=n;++i)
      {
        t=upper_bound(b+1,b+size+1,a[i])-b-1;
        fa[t]=a[i];
        a[i]=t;
      }
    t=0;
    for (i=per;i<=n;i+=per)
      block[++t]=i;
    if (block[t]!=n) block[++t]=n;
    j=1;
    for (i=1;i<=n;++i)
      {
        cnt[a[i]]++;
        if (i==block[j])
          {
            for (k=1;k<=n;++k)
              f[k][j]=cnt[k];
            j++;
          }
      }
    for (i=1;i<=t;++i)
      {
        maxn=0; ans=0;
        for (j=block[i-1]+1;j<=block[i];++j)
          if ((f[a[j]][i]-f[a[j]][i-1]>maxn)||(f[a[j]][i]-f[a[j]][i-1]==maxn&&a[j]<ans))
            {
              maxn=f[a[j]][i]-f[a[j]][i-1];
              ans=a[j];
            }
        g[i][i]=ans;
      }
    for (i=1;i<=t;++i)
      for (j=i+1;j<=t;++j)
        {
          ans=g[i][j-1];
          maxn=f[ans][j]-f[ans][i-1];
          for (k=block[j-1]+1;k<=block[j];++k)
            if ((f[a[k]][j]-f[a[k]][i-1]>maxn)||(f[a[k]][j]-f[a[k]][i-1]==maxn&&a[k]<ans))
              {
                maxn=f[a[k]][j]-f[a[k]][i-1];
                ans=a[k];
              }
          g[i][j]=ans;
        }
    for (i=1;i<=m;++i)
      {
        scanf("%d%d",&l,&r);
        l=(l+lastans-1)%n+1;
        r=(r+lastans-1)%n+1;
        if (l>r) swap(l,r);
        work(l,r);
      }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值