• 【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 imes 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('
    ');
    }
    
        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
  • 相关阅读:
    Visual Studio 2008 每日提示(四)
    修改XP注册到用户名和公司组织名
    Visual Studio技巧之打造拥有自己标识的代码模板
    收集的学习资料
    多个记录更新(存储过程)
    '1,2,3,68,10'转换为'1,2,3,6,7,8,10'
    .NET程序员面试的题一部 (转)
    [.net]DataGrid中绑定DropDownList[转]
    使用DELETE与TRUNCATE删除表所有行的区别
    sysobjects 各列的含义
  • 原文地址:https://www.cnblogs.com/Hansue/p/12957561.html
Copyright © 2020-2023  润新知