[P1972] HH necklace SDOI2009

\(Description\)

You bunch length \ (n-\) numbers, there are \ (m \) a query, each query \ (LR \) are not the same number of values.

\(Solution\)

This question obviously can not maintain direct segment tree, consider the location of every number recorded before the first occurrence of \ (POS \) , then this value into the tree line, each query \ (l \) Ranking within this range that is can (the first of several small). This is more obvious, because if a number of precursor \ (POS> L = \) , described in \ (L \) also appeared back at least once, and only this number in \ (\ LR) innermost front of it guarantee rankings \ (<l \)

so the question is transformed into an array of insert precursor, query \ (lr \) in the interval \ (l \) rankings
which is obviously still can not use the tree line to engage in direct, considering this is a bare Chairman of the tree, but the query of \ (k \) becomes the query \ (k \) ranking

void query(int u,int v,int l,int r,int k)//k是权值 
{
    if(l==r)
    {
        if(l!=k) ans+=sums[v]-sums[u];//k就是询问区间L,假如某个数出现的位置是L,那么只统计在L处的即可
        //但要注意l!=k(L)时答案要加上答案,比如询问区间大小为1的情况,直接返回就炸了 
        return;
    }
    int num=sums[lc[v]]-sums[lc[u]];
    int mid=(l+r)>>1;
    if(k>mid) ans+=num,query(rc[u],rc[v],mid+1,r,k);//在右子树中注意加上排名 
    else query(lc[u],lc[v],l,mid,k);
} 

However, it

\ (\ tm) This question data strengthen the Chairman of the tree is \ (GG \) , because this question has reached the data \ (1e6 \)
Chairman of the tree overhead space \ (nlogn \) easy to open no less than
the array open too Chairman big plus large tree in the limit constant data \ (GG \) a

I found this question turned out to be positive solutions offline + Fenwick tree!

The key is how to make the array offline, find such a nature

For a number of inquiries interval \ ([L, r] \) , if they \ (r \) are equal, then the necklace that appears with a number, it must be concerned only appears in the far right of that one

比如\(1\ 3\ 5\ 2\ 1\),对于所有\(r=5\)的询问,因为第\(5\)个数是\(1\),所以前面所有的\(1\)都不用关心,靠右边的数一定是更“优”的
所以我们每次只需要靠右的数看他能不能更新前面的数即可。
考虑使用梳妆数组维护
具体实现是按照询问的\(r\)排序,不停往右扫描,记录一个数上一次出现的位置,每次都在当前位置插入\(1\),如果出现过就在上一个出现的位置插入\(-1\)以消除上面的数的影响,扫描到一个\(r\)就进行区间求和,最后按编号输出答案就行
具体实现如下,一个简单的区间查询+单点修改

\(Code\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define maxn 1000010
#define re register
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
};
struct P{
    int l,r,id;
}node[maxn];
int tre[maxn<<2];
int tmp,n,m,ans[maxn],a[maxn],vis[maxn];
bool cmp(P A,P B)
{
    return A.r<B.r;
}
void add(int x,int k)
{
    while(x<=n)
    {
        tre[x]+=k;
        x+=(x&-x);
    }
}
int query(int x)
{
    int tmp2=0;
    while(x>=1)
    {
        tmp2+=tre[x];
        x-=(x&-x);
    }
    return tmp2;
}
int Query(int l,int r)//树状数组单点修改+区间查询 
{
   return query(r)-query(l-1);
}
int main()
{
    n=read();
    for(re int i=1;i<=n;++i) a[i]=read();
    m=read();
    for(re int i=1;i<=m;++i)
    {
        node[i].l=read(),node[i].r=read(),node[i].id=i;
    }
    sort(node+1,node+m+1,cmp);
    tmp=1;
    for(re int i=1;i<=m;++i)
    {
        for(re int j=tmp;j<=node[i].r;++j)//按r排序的原因上面说过 
        {
            if(vis[a[j]])
             add(vis[a[j]],-1);
    
             add(j,1);//不管什么时候这里都要加 
            vis[a[j]]=j;
        }
        tmp=node[i].r+1;//记录下一次开始的位置,保证扫描是$O(n)$的 
        ans[node[i].id]=Query(node[i].l,node[i].r);//记录对应编号的答案 
    }
    for(re int i=1;i<=m;++i)
     printf("%d\n",ans[i]);//按编号输出 
}

Guess you like

Origin www.cnblogs.com/Liuz8848/p/11741055.html