BZOJ5110 CodePlus2017Yazid 的新生舞会(线段树)

  考虑统计每个数字的贡献。设f[i]为前缀i中该数的出现次数,则要统计f[r]-f[l]>(r-l)/2的数对个数,也即2f[r]-r>2f[l]-l。

  注意到所有数的f的总变化次数是线性的,考虑对每次变化进行统计。

  对于当前考虑位置i,统计r∈[i,nxt[a[i]])时a[i]的贡献。如果将之前的所有2f[l]-l扔进一个线段树里,可以发现要统计的是终点在一段区间内的前缀和的和。那么不属于该区间的每个2f[l]-l都会被统计nxt[a[i]]-i次,属于该区间的2f[l]-l的被统计次数构成一个等差数列,维护区间和的同时再维护区间前缀和的和即可。统计完后把这段作为l的贡献加进去,就是一个线段树区间加。

  写的是动态开点,然而在下传标记的时候忘记开新点了,于是浪费了两个小时宝贵的人生。bzoj上T掉了然而没心情卡常了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 500010
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int n,a[N],nxt[N],p[N],f[N],cnt,root[N];
ll ans=0;
struct data{int l,r,sum,lazy;ll pre;
}tree[N*35];
void update(int &k,int l,int r,int x)
{
    if (!k) k=++cnt;
    tree[k].sum+=(r-l+1)*x;
    tree[k].pre+=1ll*x*(r-l+1)*(r-l+2)>>1;
    tree[k].lazy+=x;
}
void down(int k,int l,int r)
{
    update(tree[k].l,l,l+r>>1,tree[k].lazy),update(tree[k].r,(l+r>>1)+1,r,tree[k].lazy);
    tree[k].lazy=0;
}
int querys(int k,int l,int r,int x,int y)
{
    if (!k) return 0;
    if (l==x&&r==y) return tree[k].sum;
    if (tree[k].lazy) down(k,l,r);
    int mid=l+r>>1;
    if (y<=mid) return querys(tree[k].l,l,mid,x,y);
    else if (x>mid) return querys(tree[k].r,mid+1,r,x,y);
    else return querys(tree[k].l,l,mid,x,mid)+querys(tree[k].r,mid+1,r,mid+1,y);
}
ll querypre(int k,int l,int r,int x,int y)
{
    if (!k) return 0;
    if (l==x&&r==y) return tree[k].pre;
    if (tree[k].lazy) down(k,l,r);
    int mid=l+r>>1;
    if (y<=mid) return querypre(tree[k].l,l,mid,x,y);
    else if (x>mid) return querypre(tree[k].r,mid+1,r,x,y);
    else return querypre(tree[k].l,l,mid,x,mid)+querypre(tree[k].r,mid+1,r,mid+1,y)+1ll*querys(tree[k].l,l,mid,x,mid)*(y-mid);
}
void ins(int &k,int l,int r,int x,int y)
{
    //cout<<l-n<<' '<<r-n<<' '<<x-n<<' '<<y-n<<endl;
    if (!k) k=++cnt;
    if (l==x&&r==y) {update(k,l,r,1);return;}
    if (tree[k].lazy) down(k,l,r);
    int mid=l+r>>1;
    if (y<=mid) ins(tree[k].l,l,mid,x,y);
    else if (x>mid) ins(tree[k].r,mid+1,r,x,y);
    else ins(tree[k].l,l,mid,x,mid),ins(tree[k].r,mid+1,r,mid+1,y);
    tree[k].sum=tree[tree[k].l].sum+tree[tree[k].r].sum;
    tree[k].pre=tree[tree[k].l].pre+tree[tree[k].r].pre+1ll*tree[tree[k].l].sum*(r-mid);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5110.in","r",stdin);
    freopen("bzoj5110.out","w",stdout);
    const char LL[]="%I64d
";
#else
    const char LL[]="%lld
";
#endif
    n=read();read();
    for (int i=1;i<=n;i++)
    {
        a[i]=read()+1;
        if (!p[a[i]]) ins(root[a[i]],0,n*2,n+1-i,n);
        nxt[p[a[i]]]=i,p[a[i]]=i;
    }
    for (int i=1;i<=n;i++) if (!nxt[i]) nxt[i]=n+1;
    for (int i=1;i<=n;i++)
    {
        f[a[i]]++;
        int x=2*f[a[i]]-(nxt[i]-1),y=2*f[a[i]]-i;
        ans+=1ll*querys(root[a[i]],0,n*2,0,n+x-2)*(y-x+1);
        ans+=querypre(root[a[i]],0,n*2,n+x-1,n+y-1);
        ins(root[a[i]],0,n*2,x+n,y+n);
    }
    cout<<ans;
    return 0;
}
原文地址:https://www.cnblogs.com/Gloid/p/9897952.html