loj6253/luogu4062-Yazid的新生舞会

先考虑部分分(只有01/只有0~7)做法:枚举每个数,把和他相同的设为1,不同的设为-1,然后这个数作为众数贡献的个数就是区间和>0的个数

推着做,树状数组记前缀和<=x的区间的数量就可以,复杂度$O(8nlogn)$

如果直接套过来,$O(n^2logn)$肯定是不行的,但可以发现枚举了所有数以后1的个数一共只能有n个,而如果把相邻的-1看成一个,它的数量也是O(n)的

所以对于1的做法是不变的;对于-1,我们要连起来处理。如果设这段-1之前的和为sum,-1的个数为len可以得到这段-1的贡献,其实就是$[-n,sum-len-1]+[-n,sum-len]+...+[-n,sum-2]$(我们用一个权值线段树来记前缀和为某值的数量)

为了方便计算,做一个前缀和,就变成了

$$\sum\limits_{i=-n}^{sum-2}{\sum\limits_{j=-n}^{i}{a[j]}}-\sum\limits_{i=-n}^{sum-len-2}{\sum\limits_{j=-n}^{i}{a[j]}}$$

(可以看出与树状数组做区间加、区间求和的操作类似)

$$=(sum-2+1)\sum\limits_{i=-n}^{sum-2}{a[i]}-\sum\limits_{i=-n}^{sum-2}{i*a[i]}-((sum-len-2+1)\sum\limits_{i=-n}^{sum-len-2}{a[i]}-\sum\limits_{i=-n}^{sum-len-2}{i*a[i]})$$

然后用线段树维护a[i]和i*a[i]就可以了。而且我们每次枚举完一个以后不能直接memset,不然就变成O(n^2)了,要怎么加进来的就怎么减回去..

(然后我bzoj上就T了,肯定是评测机太卡了)

  1 #define __Ressed__ <bits/stdc++.h>
  2 #include __Ressed__
  3 #define pa pair<int,int>
  4 #define lowb(x) ((x)&(-(x)))
  5 #define REP(i,n0,n) for(i=n0;i<=n;i++)
  6 #define PER(i,n0,n) for(i=n;i>=n0;i--)
  7 #define MAX(a,b) ((a>b)?a:b)
  8 #define MIN(a,b) ((a<b)?a:b)
  9 #define CLR(a,x) memset(a,x,sizeof(a))
 10 #define rei register int
 11 using namespace std;
 12 typedef long long ll;
 13 const int maxn=5e5+5;
 14 
 15 inline ll rd(){
 16     ll x=0;char c=getchar();int neg=1;
 17     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
 18     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
 19     return x*neg;
 20 }
 21 
 22 ll v[maxn*4],iv[maxn*4],laz[maxn*4],ans;
 23 int ch[maxn*4][2],pct,root;
 24 int nxt[maxn],hd[maxn];
 25 int L,N,M,num[maxn],tmp[maxn];
 26 
 27 inline void pushdown(int p,int l,int r){
 28     int m=l+r>>1,a=ch[p][0],b=ch[p][1];
 29     if(a){
 30         laz[a]+=laz[p];
 31         v[a]+=laz[p]*(m-l+1);
 32         iv[a]+=laz[p]*(m+l)*(m-l+1)/2;
 33     }
 34     if(b){
 35         laz[b]+=laz[p];
 36         v[b]+=laz[p]*(r-m);
 37         iv[b]+=laz[p]*(r+m+1)*(r-m)/2;
 38     }
 39     laz[p]=0;
 40 }
 41 
 42 inline void update(int p){
 43     v[p]=v[ch[p][0]]+v[ch[p][1]];
 44     iv[p]=iv[ch[p][0]]+iv[ch[p][1]];
 45 }
 46 
 47 void build(int &p,int l,int r){
 48     if(!p) p=++pct;
 49     if(l<r){
 50         int m=l+r>>1;
 51         build(ch[p][0],l,m);
 52         build(ch[p][1],m+1,r);
 53     }
 54 }
 55 
 56 inline void add(int p,int l,int r,int x){
 57     laz[p]+=x;
 58     v[p]+=1ll*(r-l+1)*x;
 59     iv[p]+=1ll*(r+l)*(r-l+1)/2*x;
 60     pushdown(p,l,r);
 61 }
 62 
 63 void ins(int p,int l,int r,int x,int y,int z){
 64     if(x<=l&&r<=y){
 65         add(p,l,r,z);
 66     }else{
 67         pushdown(p,l,r);
 68         int m=l+r>>1;
 69         if(x<=m) ins(ch[p][0],l,m,x,y,z);
 70         if(y>=m+1) ins(ch[p][1],m+1,r,x,y,z);
 71         update(p);
 72     }
 73 }
 74 
 75 ll query(int p,int l,int r,int x){
 76     pushdown(p,l,r);
 77     if(r<=x){
 78         return v[p]*(x+1)-iv[p];
 79     }else{
 80         int m=l+r>>1;ll re;
 81         re=query(ch[p][0],l,m,x);
 82         if(x>=m+1) re+=query(ch[p][1],m+1,r,x);
 83         return re;
 84     }
 85 }
 86 
 87 ll query2(int p,int l,int r,int x){
 88     if(r<=x){
 89         return v[p];
 90     }else{
 91         pushdown(p,l,r);
 92         int m=l+r>>1;ll re;
 93         re=query2(ch[p][0],l,m,x);
 94         if(x>=m+1) re+=query2(ch[p][1],m+1,r,x);
 95         return re;
 96     }
 97 }
 98 
 99 int main(){
100     // freopen("6253.in","r",stdin);
101     rei i,j,k;
102     N=rd();rd();
103     for(i=1;i<=N;i++) tmp[i]=num[i]=rd();
104     sort(tmp+1,tmp+N+1);M=unique(tmp+1,tmp+N+1)-tmp-1;
105     for(i=1;i<=N;i++) num[i]=lower_bound(tmp+1,tmp+M+1,num[i])-tmp;
106     for(i=N;i;i--){
107         nxt[i]=hd[num[i]];hd[num[i]]=i;
108     }
109     L=N+1;
110     build(root,-L,L);
111     ins(root,-L,L,0,0,1);
112     for(i=1;i<=M;i++){
113         int sum=0;
114         for(j=hd[i],k=1;j;j=nxt[j]){
115             if(k<j){
116                 ans+=query(root,-L,L,sum-2)-query(root,-L,L,sum-j+k-2);
117                 ins(root,-L,L,sum-j+k,sum-1,1);
118                 sum-=j-k;
119             }sum++;
120             ans+=query2(root,-L,L,sum-1);
121             ins(root,-L,L,sum,sum,1);
122             k=j+1;
123         }
124         ans+=query(root,-L,L,sum-2)-query(root,-L,L,sum-N+k-3);
125         sum=0;
126         for(j=hd[i],k=1;j;j=nxt[j]){
127             if(k<j){
128                 ins(root,-L,L,sum-j+k,sum-1,-1);
129                 sum-=j-k;
130             }sum++;
131             ins(root,-L,L,sum,sum,-1);
132             k=j+1;
133         }
134     }
135     printf("%lld\n",ans);
136     return 0;
137 }

 

转载于:https://www.cnblogs.com/Ressed/p/9711133.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值