莫队的模板题。莫队的注意点记在蓝书上了。
本题关键的是莫队的基本写法需要记清楚
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=60005;
int n,m,c[N];
int pos[N];
ll ans,ml,mr,sum[N];
struct aa//包含询问信息的结构体
{
int l,r,id;
ll ansl,ansr;
}qu[N];
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
bool cmp1(aa a,aa b)
{
if (pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
return a.r<b.r;
}
bool cmp2(aa a,aa b)
{
return a.id<b.id;
}
void del(int i)//删除
{
ans-=sum[c[i]]*(sum[c[i]]-1)/2;
sum[c[i]]--;
ans+=sum[c[i]]*(sum[c[i]]-1)/2;
}
void ins(int i)//插入
{
ans-=sum[c[i]]*(sum[c[i]]-1)/2;
sum[c[i]]++;
ans+=sum[c[i]]*(sum[c[i]]-1)/2;
}
void solve()
{
ml=1;mr=0;//区间初始位置
for (int i=1;i<=m;i++)
{
for (;mr<qu[i].r;mr++) ins(mr+1);
for (;mr>qu[i].r;mr--) del(mr);
for (;ml<qu[i].l;ml++) del(ml);
for (;ml>qu[i].l;ml--) ins(ml-1);//莫队找区间
if (qu[i].l==qu[i].r) {qu[i].ansl=1;continue;}
if (ans==0) {qu[i].ansr=1;continue;}
qu[i].ansr=(ll)(qu[i].r-qu[i].l+1)*(qu[i].r-qu[i].l)/2;//这里必须要加ll
qu[i].ansl=ans;
ll d=gcd(qu[i].ansl,qu[i].ansr);
qu[i].ansl=qu[i].ansl/d;
qu[i].ansr=qu[i].ansr/d;
}
}
int main()
{
scanf("%d%d",&n,&m);
int block=int(sqrt(n*1.0));
for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1//分块
for (int i=1;i<=n;i++) scanf("%d",&c[i]);
for (int i=1;i<=m;i++) scanf("%d%d",&qu[i].l,&qu[i].r),qu[i].id=i;
sort(qu+1,qu+m+1,cmp1);
solve();
sort(qu+1,qu+m+1,cmp2);
for (int i=1;i<=m;i++) printf("%lld/%lld\n",qu[i].ansl,qu[i].ansr);
return 0;
}
总结
关于这道题,有一个地方必须要开ll,注意这里的n可能达到50000,而50000^2>maxint,会爆int的。
刚开始想到有可能爆int错了,但是没有考虑清楚,找到爆点。实际上。以后关于这些乘法呀加法呀要更加注意爆int。!!!!