[SNOI2017] 一个简单的询问
我们看看原式子
设 get(l,r,x) 表示x 在l,r中出现的次数:
=)
这也太多有关量了我们尝试减少一点
式子变成了同一形式,
我们可以拆分为四个问题 ,,,
问题的有关量减少了2个,将一个问题拆分成更多的问题,但减少了有关量这是在信息学中很重要的思想。
根据定义理解一下,现在他可不是一个区间而是两个前缀,就是:
求每出现的数分别在两前缀的次数乘积 再求和。
莫队好像只能解决区间问题吧,这是前缀怎么办?
莫队算法的本质是 1.改变你的暴力的顺序使你跑的更快。2.在平面是有一些点 点的哈弗曼距离的路径点覆盖尽可能小。和区间是绑定的吗?回答我!look my eyes! tell me why?
回到正题,我们想暴力怎么做,我觉得我会先把一个前缀的数放桶里,遍历一遍另一前缀直接加。暴力想到,一个数增减 只需要增减另一个前缀的数。开两个桶结束咯。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int NR=4e5+5;
int n,m,k,s[NR],t[NR],u[NR],v[NR],l[NR],r[NR],MX,z[NR],ans[NR],a[NR],c[NR],d[NR],sum=0,g[NR];
bool cmp(int a,int b){
return (l[a]/MX!=l[b]/MX)?(l[a]/MX<l[b]/MX):(((l[a]/MX)&1)?r[a]<r[b]:r[a]>r[b]);
} void Nw(int x,int y){
++m;
l[m]=x;r[m]=y;z[m]=m;
}
void Ad1(int x){
c[x]++;sum+=d[x];
}
void Da1(int x){
c[x]--;
sum-=d[x];
}
void Ad2(int x){
d[x]++;sum+=c[x];
}
void Da2(int x){
d[x]--;sum-=c[x];
}
signed main(){
cin>>n;MX=sqrt(n);
for(int i=1;i<=n;i++)cin>>a[i];
int sm;
cin>>sm;
for(int i=1;i<=sm;i++){
cin>>s[i]>>t[i]>>u[i]>>v[i];
Nw(t[i],v[i]);
}
sort(z+1,z+m+1,cmp);
for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
while(R<r[Z]){
Ad1(a[++R]);
}
while(R>r[Z]){
Da1(a[R--]);
}
while(L>l[Z]){
Da2(a[L--]);
}
while(L<l[Z]){
Ad2(a[++L]);
}
ans[Z]+=sum;
}
m=0;
for(int i=1;i<=n;i++)c[i]=d[i]=0;
for(int i=1;i<=sm;i++){
Nw(s[i]-1,v[i]);
}sum=0;
sort(z+1,z+m+1,cmp);
for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
while(R<r[Z]){
Ad1(a[++R]);
}
while(R>r[Z]){
Da1(a[R--]);
}
while(L>l[Z]){
Da2(a[L--]);
}
while(L<l[Z]){
Ad2(a[++L]);
}
ans[Z]-=sum;
}
m=0;
for(int i=1;i<=n;i++)c[i]=d[i]=0;
for(int i=1;i<=sm;i++){
Nw(t[i],u[i]-1);
}sum=0;
sort(z+1,z+m+1,cmp);
for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
while(R<r[Z]){
Ad1(a[++R]);
}
while(R>r[Z]){
Da1(a[R--]);
}
while(L>l[Z]){
Da2(a[L--]);
}
while(L<l[Z]){
Ad2(a[++L]);
}
ans[Z]-=sum;
}
m=0;
for(int i=1;i<=n;i++)c[i]=d[i]=0;
for(int i=1;i<=sm;i++){
Nw(s[i]-1,u[i]-1);
}sum=0;
sort(z+1,z+m+1,cmp);
for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
while(R<r[Z]){
Ad1(a[++R]);
}
while(R>r[Z]){
Da1(a[R--]);
}
while(L>l[Z]){
Da2(a[L--]);
}
while(L<l[Z]){
Ad2(a[++L]);
}
ans[Z]+=sum;
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}
[国家集训队] 数颜色 / 维护队列
一稿40pts
带修莫队。
把时间轴看成地三维跑莫队。
#include<bits/stdc++.h>
using namespace std;
const int NR=1.5e5+5;
int n,m,l[NR],r[NR],MX,z[NR],ans[NR],a[NR],c[NR],sum=0,t[NR],p[NR],to[NR],fr[NR],tmp[NR];
bool cmp(int a,int b){
if(l[a]/MX!=l[b]/MX)return l[a]/MX<l[b]/MX;
else if(t[a]/MX!=t[b]/MX)return t[a]/MX<t[b]/MX;
else return r[a]<r[b];
} void Add(int x){
c[x]++;
if(c[x]==1)sum++;
} void Del(int x){
c[x]--;
if(c[x]==0)sum--;
}
signed main(){
int q;
cin>>n>>q;MX=pow(n,0.66);
for(int i=1;i<=n;i++){
cin>>a[i];tmp[i]=a[i];
}
for(int i=1;i<=q;i++){
char C;
cin>>C;
if(C=='Q'){
m++;
cin>>l[m]>>r[m];t[m]=i;z[m]=m;
}else{
cin>>p[i]>>to[i];
fr[i]=a[i];
a[i]=to[i];
}
}
sort(z+1,z+m+1,cmp);for(int i=1;i<=n;i++)a[i]=tmp[i];
for(int i=1,Z,L=1,R=0,T=0;i<=m;i++){Z=z[i];
while(T<t[Z]){
++T;
a[p[T]]=to[T];
}
while(T>t[Z]){
--T;
a[p[T]]=fr[T];
}
while(R<r[Z])Add(a[++R]);
while(L>l[Z])Add(a[--L]);
while(L<l[Z])Del(a[L++]);
while(R>r[Z])Del(a[R--]);
ans[Z]=sum;
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}
[HNOI2016] 大数
一稿96pts
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int NR=2e5+5;
int p,n,m,MX,h[NR],a[NR],t[NR],cnt[NR],l[NR],r[NR],z[NR],sum=0,ans[NR],w[NR];
string S;
bool cmp(int a,int b){
return (l[a]/MX!=l[b]/MX)?(l[a]/MX<l[b]/MX):(((l[a]/MX)&1)?r[a]<r[b]:r[a]>r[b]);
}
void Add(int x){
sum+=cnt[x]++;
} void Del(int x){
sum-=--cnt[x];
}
signed main(){
cin>>p;
cin>>S;n=S.size();MX=sqrt(n);
for(int i=0;i<n;i++)a[i+1]=S[i]-'0';w[0]=1;
for(int i=1;i<=n;i++)w[i]=(w[i-1]*10)%p;
for(int i=1;i<=n;i++)h[i]=(h[i-1]*10+a[i])%p;
for(int i=1;i<=n;i++)t[i]=h[i]=(h[i]*w[n-i])%p;
sort(t+1,t+n+2);
int q=unique(t+1,t+n+2)-t-1;
for(int i=1;i<=n;i++)h[i]=lower_bound(t+1,t+q+1,h[i])-t;
cin>>m;
for(int i=1;i<=m;z[i]=i,i++)cin>>l[i]>>r[i];
sort(z+1,z+m+1,cmp);
cnt[1]=1;h[0]=1;
for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
while(R<r[Z])Add(h[++R]);
while(L>=l[Z])Add(h[--L]);
while(L<l[Z]-1)Del(h[L++]);
while(R>r[Z])Del(h[R--]);
ans[Z]=sum;
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}
错音:没有意识到2和5与10不是互质的,使用了进制哈希导致错误。
做过字符串hash的人会想到进制哈希。一个区间[l,r]的值就是这个值被整除那就写成同余形式吧
两边写成相同形式了,,
,问题变成A题弱化版,看有几个和相同
注意到2和5需要特判,直接看末尾即可。