[线段树]「CodePlus 2017 11 月赛」Yazid 的新生舞会

每种颜色单独做

枚举右端点,另 pi 表示前缀 i 中,当前颜色的个数, qi 表示前缀 i 中不是当前颜色的个数

那么答案就要加上满足 prqr>pl1ql1 l <script id="MathJax-Element-214" type="math/tex">l</script> 的个数

这个东西线段树大力搞吧……

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int N=1000010;

int n,a[N];
vector<int> b[N];

ll tag[N<<2],tot[N<<2],sum[N<<2],L[N<<2],R[N<<2];
int c[N<<2];

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

void rebuild(int g,int l,int r){
  L[g]=l; R[g]=r; tot[g]=tag[g]=sum[g]=0;
  if(l==r) return ;
  int mid=l+r>>1;
  rebuild(g<<1,l,mid); rebuild(g<<1|1,mid+1,r);
}

inline void add(int g,int x){
  tag[g]+=x; sum[g]+=(R[g]-L[g]+1)*x; tot[g]+=(2*n+2-L[g]-R[g])*(R[g]-L[g]+1)/2*x;
}

inline void clear(int g){
  sum[g]=tot[g]=tag[g]=0; c[g]=1;
}

inline void Push(int g){
  if(c[g])
    clear(g<<1),clear(g<<1|1),c[g]=0;
  if(tag[g])
    add(g<<1,tag[g]),add(g<<1|1,tag[g]),tag[g]=0;
}

inline void Up(int g){
  tot[g]=tot[g<<1]+tot[g<<1|1];
  sum[g]=sum[g<<1]+sum[g<<1|1];
}

void Add(int g,int l,int r,int L,int R){
  if(L==l && r==R) return add(g,1);
  int mid=L+R>>1; Push(g);
  if(r<=mid) Add(g<<1,l,r,L,mid);
  else if(l>mid) Add(g<<1|1,l,r,mid+1,R);
  else Add(g<<1,l,mid,L,mid),Add(g<<1|1,mid+1,r,mid+1,R);
  Up(g);
}

typedef pair<ll,ll> PAR;

inline PAR operator +(PAR a,PAR b){
  return PAR(a.first+b.first,a.second+b.second);
}

PAR Query(int g,int l,int r,int L,int R){
  if(l>r) return PAR(0,0);
  if(l==L && r==R) return PAR(tot[g],sum[g]);
  int mid=L+R>>1; Push(g);
  if(r<=mid) return Query(g<<1,l,r,L,mid);
  else if(l>mid) return Query(g<<1|1,l,r,mid+1,R);
  else return Query(g<<1,l,mid,L,mid)+Query(g<<1|1,mid+1,r,mid+1,R);
}

int main(){
  freopen("6253.in","r",stdin);
  freopen("6253.out","w",stdout);
  read(n); int tp; read(tp);
  for(int i=1;i<=n;i++)
    read(a[i]),b[a[i]].push_back(i);
  ll ans=0;
  rebuild(1,-n,n);
  for(int x=0;x<n;x++){
    if(!b[x].size()) continue;
    clear(1); Add(1,-b[x][0]+1,0,-n,n);
    b[x].push_back(n+1);
    int pre=-b[x][0]+1;
    for(int i=0;i<b[x].size()-1;i++){
      pre++; int delt=b[x][i+1]-b[x][i]-1;
      ans+=Query(1,-n,pre-delt-1,-n,n).second*(delt+1);
      PAR cur=Query(1,pre-delt,pre-1,-n,n);
      ans+=cur.first-cur.second*(n-pre+1);
      Add(1,pre-delt,pre,-n,n);
      pre-=delt;
    }
  }
  cout<<ans<<endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值