【HEOI2012】采花

题目链接

考虑把每种颜色的贡献放到一个位置来算,当然,要保证该颜色至少出现两次。

发现无论是扫描左端点并在区间第一次出现时取贡献,还是扫描右端点并在区间最后一次出现时取贡献,都没办法用树状数组很方便地维护,因为我们是取区间和来当做答案的,也就是对于某颜色来说,关于固定端点,不固定的那个端点必须比该颜色第二远的出现位置更远。考虑放在区间里第二次出现某种颜色的花的位置来算,那么$(1,2,3,2,3,4,1,1)$就会转化成$(0,1,1,0,0,0,1,0)$(询问区间的左端点是$1$或$2$时),不断扫描过左端点,利用树状数组动态维护前缀和,每次询问右端点即可。

考虑把询问按照区间左端点升序排序,接下来看怎么在区间左端点移动的情况下维护树状数组,以便我们询问右端点作为答案。

设$pre_i$,$suc_i$分别表示$i$号花同颜色的前驱和后继。

首先我们要理解:我们的当前左端点$l$是正在两个排序好的询问区间的左端点间移动的,也就是说,$l$号花是不能计入答案,也不能算进$x_l$的个数的,同时它可以滚动更新。

那么我们要保证一朵花至少出现两次,它的位置就是$suc[\,suc[l]\,]$(如果存在的话)。同时,既然我们在$suc[\,suc[l]\,]$处加一了,就必须在$suc[l]$处减一(如果存在的话),因为$suc[l]$也做过$pre[l]$的二级后继,也即$suc[l]=suc[\,suc[\,pre[l]\,]\,]$时,$suc[l]$处是有加过一的。当然,我们要先把全局第二次出现的花的位置加一,因为它们没有二级前驱来给自己加一。注意:$l$只能移动到下一个询问区间的左端点减一。

还有一点,就是本题开了两档数据,第二档是$2\times 10^6$的规模,也就是卡常了。我这里用的是树状数组,用了快读快写和register才过。这里放几个截图。

无常数优化:

加入快读和register:

再加入快写:

代码(100分):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define RI RG int
#define _1 first
#define _2 second
using namespace std;
const int N=2e6;

IL void read(RI &x){
    x=0;    RG char ch=getchar();
    while(!isdigit(ch))    ch=getchar();
    for(;isdigit(ch);ch=getchar())    x=(x<<3)+(x<<1)+ch-'0';
}

IL void write(RI x){
    RI k=0,s[13];
    if(x)    for(;x;x/=10)    s[++k]=x%10;
    else     s[++k]=0;
    while(k)    putchar(s[k--]+'0');
}

IL void writeln(RI x){
    write(x);    putchar('\n');
}

    int n,c,m,x[N+3];
    
struct Qry{
    int l,r,d;
}a[N+3];

IL bool operator<(RG Qry x,RG Qry y){
    return x.l<y.l;
}

    int s[N+3];

IL int lowbit(RI x){
    return x&(-x);
}

IL void mdf(RI p,RI x){
    for(;p<=n;p+=lowbit(p))
        s[p]+=x;
}

IL int qry(RI p){
    RI ret=0;
    for(;p;p-=lowbit(p))
        ret+=s[p];
    return ret;
}

    int pre[N+3],suc[N+3],col[N+3];

IL void init(){
    for(RI i=1;i<=n;i++){
        if(col[x[i]])
            pre[i]=col[x[i]];
        col[x[i]]=i;
        
    }
    
    memset(col,0,sizeof col);
    for(RI i=n;i>=1;i--){
        if(col[x[i]])
            suc[i]=col[x[i]];
        col[x[i]]=i;
        
    }
    
    for(RI i=1;i<=n;i++)
    if(!pre[i]&&suc[i])
        mdf(suc[i],1);
    
    sort(a+1,a+m+1);
    
}

    int ans[N+3];

int main(){
    read(n);    read(c);    read(m);
    for(RI i=1;i<=n;i++)
        read(x[i]);
    for(RI i=1;i<=m;i++){
        read(a[i].l);    read(a[i].r);    a[i].d=i;
    }
    
    init();
    for(RI i=1;i<=m;i++){
        for(RI j=a[i-1].l;j<a[i].l;j++)
        if(suc[j]){
            mdf(suc[j],-1);
            if(suc[suc[j]])
                mdf(suc[suc[j]],1);
            
        }
        ans[a[i].d]=qry(a[i].r);
        
    }
    
    for(RI i=1;i<=m;i++)
        writeln(ans[i]);

    return 0;

}
View Code

猜你喜欢

转载自www.cnblogs.com/Hansue/p/12957561.html
今日推荐