大意:
一段数组,每次查询一个区间,询问区间内的数字种类数。
思路:
这道题其实做法比较多
莫队/主席树(当然我还不会。。。)/树状数组都可以。
先讲一下树状数组的做法:
采用离线+树状数组求前缀和
因为询问比较多,在线做的话用树状数组属实是扛不住,所以离线,然后按r从小到大排序。
那么对于每一次从查询【l,r】,以及我们当前查到的w:
如果w对应的数k之前是没有出现过的,那么直接将这一点w的权值记为1即可,后面树状数组维护前缀和即可。但k如果之前在w'位置出现过,那么我们就将这一点w的权值记为1,bing将w’的权值记为0.因为w'的权值贡献在之前的查询中肯定是计算过了,但它不一定对当前的【l,r】区间有贡献,但我们能确定w一定对【l,r】有贡献,所以做如上操作。并且由于是按查询的r从小到大排序,所以将w’的权值记为0对后面没有影响。
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(x) x&(-x)
const ll N=1e6+10;
ll tr[N];
struct ty
{
ll id;//编号
ll l,r;//左右界
}num[N];
ll ans[N];
bool cmp(ty a,ty b){
return a.r<b.r;
}
ll n,m;
ll a,b;
ll id[N];
void add(ll x,ll y)
{
while(x<=n)
{
tr[x]+=y;
x+=lowbit(x);
}
}
ll sum(ll x)
{
ll ans=0;
while(x>0)
{
ans+=tr[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;++i)
{
scanf("%lld",&id[i]);
}
scanf("%lld",&m);
for(int i=1;i<=m;++i)
{
scanf("%lld%lld",&a,&b);
num[i]={i,a,b};
}
sort(num+1,num+1+m,cmp);
map<ll,ll> mp;
for(int i=1,j=1;i<=m;++i)
{
ll l=num[i].l,r=num[i].r,idd=num[i].id;
while(j<=r)
{
if(mp[id[j]]) add(mp[id[j]],-1);
mp[id[j]]=j;
add(j,1);
j++;
}
ans[idd]=sum(r)-sum(l-1);
}
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}
然后是莫队的做法:
莫队
那么这个题对于莫队来说就是一道板子题了。。。
就硬套板子就行了。
注意分块别写错,以及移动l,r时的小trick
//在当前范围内,是先移动再处理
while(l>num[i].l) move(--l,1);//--l
while(r<num[i].r) move(++r,1);//++r
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(x) x&(-x)
const ll N=1e6+10;
ll tr[N];
ll n,m;
struct ty
{
ll id;//编号
ll l,r;//左右界
}num[N];
ll wid[N];//记录种类
ll id[N];//分块用
ll ans[N];//记录答案
ll cnt[N];//记录每一个种类出现的次数
bool cmp(ty a,ty b){
if(id[a.l]==id[b.l])
return a.r<b.r;
return a.l<b.l;
}
ll anss=0;
void block()
{
ll kk=sqrt(n);
for(int i=1;i<=n;++i)
{
id[i]=(i-1)/kk+1;
}
}
void move(ll pos/*当前位置*/,ll op)
{
if(op==0)
{
cnt[wid[pos]]--;
if(cnt[wid[pos]]==0) anss--;
return;
}
if(op==1)
{
cnt[wid[pos]]++;
if(cnt[wid[pos]]==1) anss++;
return;
}
}
inline int read(){
int sgn = 1; int cnt = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-')
sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
cnt = cnt*10 + (ch-'0');
ch = getchar();
}
return sgn*cnt;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
wid[i]=read();
}
block();
m=read();
ll a,b;
for(int i=1;i<=m;++i)
{
a=read();
b=read();
num[i]={i,a,b};
}
sort(num+1,num+1+m,cmp);
ll l=0,r=0;
anss=0;
for(int i=1;i<=m;++i)
{
while(l<num[i].l) move(l++,0);
while(l>num[i].l) move(--l,1);//--l
while(r<num[i].r) move(++r,1);//++r
while(r>num[i].r) move(r--,0);
ans[num[i].id]=anss;
}
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}
以及,这题有输入输出挂。
到这里这题就算结束了,再上一道莫队的题。
Powerful array - CodeForces 86D - Virtual Judge
大意差不多,就是查询的东西不一样
每次查询区间【l,r】,每一个数m出现的次数为k,则其贡献为k^2*m,就区间总贡献。
那么每次数字的次数+1时,贡献就加上m*(2*k+1)
每次数字的次数-1时,贡献就减去m*(2*k-1)
改变一下移动的处理操作即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(x) x&(-x)
const ll N=1e6+10;
ll tr[N];
ll n,m;
struct ty
{
ll id;//编号
ll l,r;//左右界
}num[N];
ll wid[N];//记录种类
ll id[N];//分块用
ll ans[N];//记录答案
ll cnt[N];//记录每一个种类出现的次数
bool cmp(ty a,ty b){
if(id[a.l]==id[b.l])
return a.r<b.r;
return a.l<b.l;
}
ll anss=0;
void block()
{
ll kk=sqrt(n);
for(int i=1;i<=n;++i)
{
id[i]=(i-1)/kk+1;
}
}
void move(ll pos/*当前位置*/,ll op)
{
if(op==0)
{
anss-=(cnt[wid[pos]]*2-1)*wid[pos];
cnt[wid[pos]]--;
return;
}
if(op==1)
{
anss+=(cnt[wid[pos]]*2+1)*wid[pos];
cnt[wid[pos]]++;
return;
}
}
inline int read(){
int sgn = 1; int cnt = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-')
sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
cnt = cnt*10 + (ch-'0');
ch = getchar();
}
return sgn*cnt;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=n;++i)
{
wid[i]=read();
}
block();
ll a,b;
for(int i=1;i<=m;++i)
{
a=read();
b=read();
num[i]={i,a,b};
}
sort(num+1,num+1+m,cmp);
ll l=0,r=0;
anss=0;
for(int i=1;i<=m;++i)
{
while(l<num[i].l) move(l++,0);
while(l>num[i].l) move(--l,1);//--l
while(r<num[i].r) move(++r,1);
while(r>num[i].r) move(r--,0);
ans[num[i].id]=anss;
}
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}