小B的询问
题目链接:https://www.luogu.com.cn/problem/P2709
大意
给定一个区间,每次询问区间[l,r]中不同元素出现次数的平方和。
思路
静态区间可使用莫队算法离线操作
将每个查询区间按分块的区域排序,定义一个双指针,通过add和sub操作维护输出值。
莫队板子题
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=50007;
ll a[N];
ll ans[N];
ll pos[N];
ll cnt[N];
struct node
{
ll l,r,k;
}q[N];
ll res;
bool cmp(node &f,node &s)
{
return pos[f.l]==pos[s.l]?f.r<s.r:pos[f.l]<pos[s.l];
}
void Add(ll p)
{
cnt[a[p]]++;
res+=cnt[a[p]]*cnt[a[p]]-(cnt[a[p]]-1)*(cnt[a[p]]-1);
}
void Sub(ll p)
{
res-=cnt[a[p]]*cnt[a[p]]-(cnt[a[p]]-1)*(cnt[a[p]]-1);
cnt[a[p]]--;
}
int main()
{
ll n,m,k;
cin>>n>>m>>k;
//分块
ll siz=sqrt(n);
for(ll i=1;i<=n;i++)
{
cin>>a[i];
pos[i]=i/siz;
}
for(ll i=0;i<m;i++)
{
cin>>q[i].l>>q[i].r;
q[i].k=i;
}
sort(q,q+m,cmp);
ll l=1,r=0;
for(ll i=0;i<m;i++)
{
while(q[i].l<l)
Add(--l);
while(q[i].r>r)
Add(++r);
while(q[i].l>l)
Sub(l++);
while(q[i].r<r)
Sub(r--);
ans[q[i].k]=res;
}
for(ll i=0;i<m;i++)
cout<<ans[i]<<endl;
return 0;
}
zoto
题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=6959
大意
给定一个数组,求区间[x0,x1]中值域在[y0,y1]之间的不同元素个数
思路
主席树也不知道假不假,标程是莫队那就莫队吧
具体模板和上面的题类似,具体更改add和sub函数
建一个树状数组用来维护值域[1,n]中不同元素的个数(前缀和),注意题目中值域可能出现0,为了树状数组的可使用我们可以将所有值都强制+1.
怎么保证存到树状数组内的是不同元素个数呢,我们可以用数组记录当前出现元素的个数,当add操作中元素个数为1时存入当前位置1,当sub操作中元素个数为0时存入当前位置-1.(就是这个当前位置强制+1)
莫队大概也就是个暴力(贪心)?一般静态情况修改add和sub操作就够了,非静态也没写过,碰到再学。
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=100007;
int a[N];
int ans[N];
int pos[N];
int cnt[N];
int v[N];
int sum(int i){
int res=0;
for(;i>0;i-=i&-i) res+=v[i];
return res;
}
void add(int i,int x){
for(;i<N;i+=i&-i) v[i]+=x;
}
struct node
{
int l,r,ql,qr,k;
}q[N];
bool cmp(node f,node s)
{
return pos[f.l]==pos[s.l]?(pos[f.l]&1?f.r<s.r:f.r>s.r):pos[f.l]<pos[s.l];
}
void Add(int p)
{
cnt[a[p]]++;
if(cnt[a[p]]==1)
add(a[p]+1,1);
}
void Sub(int p)
{
cnt[a[p]]--;
if(cnt[a[p]]==0)
{
add(a[p]+1,-1);
}
}
int main()
{
//ios::sync_with_stdio(0);
int t;cin>>t;
while(t--)
{
memset(v,0,sizeof(v));
memset(cnt,0,sizeof(cnt));
memset(ans,0,sizeof(ans));
memset(pos,0,sizeof(pos));
int n,m;
scanf("%d %d",&n,&m);
//分块
int siz=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[i]=i/siz;
}
for(int i=0;i<m;i++)
{
scanf("%d %d %d %d",&q[i].l,&q[i].ql,&q[i].r,&q[i].qr);
q[i].k=i;
}
sort(q,q+m,cmp);
int l=1,r=0;
for(int i=0;i<m;i++)
{
while(q[i].l<l)
Add(--l);
while(q[i].r>r)
Add(++r);
while(q[i].l>l)
Sub(l++);
while(q[i].r<r)
Sub(r--);
ans[q[i].k]=sum(q[i].qr+1)-sum(q[i].ql-1+1);
}
for(int i=0;i<m;i++)
cout<<ans[i]<<endl;
}
return 0;
}