zoukankan      html  css  js  c++  java
  • 「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

      学习了新姿势。。(一直看不懂大爷的代码卡了好久T T

      首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2*s_l-l$的区间$[l+1,r]$其众数为$x$,这个显然可以用一个数据结构来维护。

      直接扫一遍效率是$O($数字种类数$*nlogn)$的,无法承受,但是我们发现,对于每一段非$x$的数,$2*s_i-i$是公差为$-1$的等差数列,所以它们对答案的贡献实际上可以一次性计算。设$L$为一段非$x$数的开头,$R$为结尾,则$leq 2*s_R-R$的数贡献会被计算$len$次,$2*s_{R-1}-(R-1)$的数的贡献会被计算$len-1$次,...,$s_l-l$的数的贡献会被计算$1$次,这个贡献的计算次数也是个等差数列。

      那实际上我们有三种维护这个的方法。

      ①维护$a_i$表示$2*s_i-i$的出现次数,支持区间加和区间查询$sum_{i=l}^r a_i*(i-l+1)$,较为麻烦,权值线段树。

      ②维护$s_i$表示$a_i$的前缀和,支持区间加一段等差数列和区间查询,挺可写,权值线段树。

      ③维护$s_i$的前缀和,支持区间加二次函数和单点查询,代码短但因为较抽象所以有些难调,树状数组,非常快。

      这里只说第三种写法,第一次见到这种操作...

      树状数组里实际上维护的是$s_1,s_1+s_2,s_1+s_2+s_3,...$,所以修改一段区间的时候,相当于给一段区间加上等差数列的求和,即$((1+i-l+1)*(i-l+1))/2=(i^2+(3-2l)i+l^2+3l+2)/2$,所以我们只要在树状数组上维护二次项,一次项和常数项,区间修改用差分,最后查询一段区间只要头尾相减就好了...

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #define ll long long
    using namespace std;
    const int maxn=1000010;
    int n, x, N, t;
    int a[maxn], treety[maxn], pos[maxn], pre[maxn];
    ll tree1[maxn], tree2[maxn], tree3[maxn], ans;
    char buf[20000010],*ptr=buf-1;
    inline int read()
    {
        char c=*++ptr;int s=0,t=1;
        while(c<48||c>57)c=*++ptr;
        while(c>=48&&c<=57){s=s*10+c-'0';c=*++ptr;}
        return s*t;
    }
    inline void add(int x, int p)
    {
        x+=n+1;
        ll delta1=1, delta2=3-2*x, delta3=1ll*x*x-3*x+2;
        for(;x<=N;x+=x&-x)
        { 
            if(treety[x]!=t) treety[x]=t, tree1[x]=tree2[x]=tree3[x]=0;
            tree1[x]+=delta1*p, tree2[x]+=delta2*p, tree3[x]+=delta3*p;
        }
    }
    inline void query(int x, int ty)
    {
        x+=n+1; int pos=x;
        for(;x;x-=x&-x) if(treety[x]==t) 
        ans+=(tree1[x]*pos*pos+tree2[x]*pos+tree3[x])*ty;
    }
    inline void update(int l, int r, int s){add(2*s-r, 1); add(2*s-l+1, -1);}
    int main()
    {
        fread(buf,1,sizeof(buf),stdin); n=read(); x=read(); N=n+n+2;
        for(int i=1;i<=n;i++) x=read(), pre[i]=pos[x], pos[x]=i;
        for(int i=0;i<n;i++)
        if(pos[i])
        {
            int cnt=0; ++t;
            for(int j=pos[i];j;j=pre[j]) a[++cnt]=j;
            update(0, a[cnt]-1, 0);
            for(int j=cnt;j;j--) 
            {
                query(2*(cnt-j+1)-a[j]-1, 1);
                query(2*(cnt-j+1)-((j-1)?a[j-1]+1:n+2), -1);
                update(a[j], (j-1)?a[j-1]-1:n, cnt-j+1);
            }
        }
        printf("%lld
    ", ans>>1);
    }
    View Code
  • 相关阅读:
    android xml 布局错误
    java int与integer的区别
    android html.fromHtml 用例
    Android 手势操作识别
    android android 判断是否滑动
    Android 通过 Intent 传递类对象或list对象
    android 学习JSON
    android 解决ListView点击与滑动事件冲突
    关于android的日志输出&LogCat
    android ListView 属性
  • 原文地址:https://www.cnblogs.com/Sakits/p/8137882.html
Copyright © 2011-2022 走看看