复杂度n^(1.5)
四个while循环中注意边界处的讨论。
精髓在add和del函数还有其排序的方式。
1.莫队求解l到r中不同数的个数。
1) Fast Queries 题目链接:https://vjudge.net/problem/LightOJ-1188
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#pragma GCC optimize(2)
const int maxx=1e6+19;
int pos[maxx];// 那一块
int a[maxx];// 原数组
struct node // 询问
{
int l,r,id;
}Q[maxx];
bool cmp(node a,node b)
{
if(pos[a.l]==pos[b.l]) return a.r<b.r;//先按l所在的块排,如果相等就按r排
else return pos[a.l]<pos[b.l];
}
int ans[maxx];// 最终答案
ll size,Ans=0; // 每次转换得到的值.
int flag[maxx*2]; // 标记数字出现的次数
// 莫队算法由L-R 转换为 (l-1,R) (l+1,R) (L,R-1) (L,R+1)
void add(int x)
{
flag[a[x]]++;
if(flag[a[x]]==1) Ans++;
}
void del(int x)
{
flag[a[x]]--;
if(flag[a[x]]==0) Ans--;
}
int main()
{
int n,m,t;
int L,R;
scanf("%d",&t);
int p=0;
while(t--)
{
L=1,R=0;
Ans=0;
memset(flag,0,sizeof(flag));
scanf("%d%d",&n,&m);
size=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[i]=i/size;//别写错地方了!》。。。。
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+1+m,cmp);
for(ll i=1;i<=m;i++)
{
while(R<Q[i].r)
{
R++;
add(R);
}
while(L>Q[i].l) ///前缀和L+1>Q[i].l
{
L--;
add(L);
}
while(L<Q[i].l)///前缀和L+1<Q[i].l
{
del(L);
L++;
}
while(R>Q[i].r)
{
del(R);
R--;
}
ans[Q[i].id]=Ans;
}
printf("Case %d:\n",++p);
for(int i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}
2.莫队求解l到r中有多少个异或和等于k
1) F - XOR and Favorite Number 题目链 https://vjudge.net/contest/326214#problem/F
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// ai^ai+1...^aj=sumi-1^sumj sum为异或前缀和.
// 莫队算法由L-R 转换为 (l-1,R) (l+1,R) (L,R-1) (L,R+1)
typedef long long ll;
#pragma GCC optimize(2)
const int maxx=1e6+19;
int pos[maxx];// 那一块
ll a[maxx];// 原数组
struct node // 询问
{
int l,r,id;
}Q[maxx];
bool cmp(node a,node b)
{
if(pos[a.l]==pos[b.l]) return a.r<b.r;//先按l所在的块排,如果相等就按r排
else return pos[a.l]<pos[b.l];
}
ll ans[maxx];// 最终答案
ll size,Ans=0; // 每次转换得到的值.
int flag[maxx*2]; // 标记数字出现的次数
int n,m,t,k;
void add(int x)
{
Ans+=flag[a[x]^k];// 假设这一段新增加的数量为x 次数为a也就是flag[ax]的个数 则a^x=k 两边同时异或a 得x=k^a
flag[a[x]]++;
}
void del(int x)
{
flag[a[x]]--;
Ans-=flag[a[x]^k];
}
int main()
{
int L,R;
while(~scanf("%d%d%d",&n,&m,&k))
{
L=1,R=0;
Ans=0;
memset(flag,0,sizeof(flag));
size=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]^=a[i-1];
pos[i]=i/size;//别写错地方了!》。。。。
}
flag[0]=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+1+m,cmp);
for(int i=1;i<=m;i++)
{
while(L<Q[i].l)// ai^ai+1...^aj=sumi-1^sumj 答案=l-1到r的值
{
del(L-1);
L++;
}
while(L>Q[i].l)
{
L--;
add(L-1);
}
while(R<Q[i].r)
{
R++;
add(R);
}
while(R>Q[i].r)
{
del(R);
R--;
}
ans[Q[i].id]=Ans;
}
for(int i=1;i<=m;i++)
{
printf("%lld\n",ans[i]);
}
}
return 0;
}
3.小Z的袜子
就是组合数求概率然后用莫队处理一下下。
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
const int maxx=5e4+19;
#define ll long long
ll a[maxx];
struct node
{
ll l,r,id;
}Q[maxx];
ll pos[maxx];
ll size=0,Ans=0;
ll ans1[maxx],ans2[maxx];
ll flag[maxx];
ll L=1,R=0;
void add(ll x)
{
flag[a[x]]++;
if(flag[a[x]]>=2)
{
ll p=flag[a[x]];
Ans+=((p*(p-1))/2);// 比如由c32变成了 c42 Ans变化就是加上c42减去c32;
Ans-=(((p-1)*(p-2))/2);
}
}
void del(ll x)
{
// 和加思路一样的。
if(flag[a[x]]>=2) // 如果对答案有贡献
{
ll p=flag[a[x]];
Ans-=((p*(p-1))/2);
Ans+=(((p-1)*(p-2))/2);
}
flag[a[x]]--;
}
bool cmp(node a,node b)
{
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
ll gcd(ll a,ll b)
{
if(b==0) return a;
return gcd(b,a%b);
}
int main()
{
ll n,m,k,i,j,t;
scanf("%lld%lld",&n,&m);
size=sqrt(n*1.0);
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(int)i/size;
}
for(i=1;i<=m;i++)
{
scanf("%lld%lld",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+1+m,cmp);
for(i=1;i<=m;i++)
{
while(L<Q[i].l)
{
del(L);
L++;
}
while(L>Q[i].l)
{
L--;
add(L);
}
while(R<Q[i].r)
{
R++;
add(R);
}
while(R>Q[i].r)
{
del(R);
R--;
}
ans1[Q[i].id]=Ans;
ans2[Q[i].id]=(R-L)*(R-L+1)/2; // 分母.
}
for(i=1;i<=m;i++)
{
ll x=gcd(ans1[i],ans2[i]);
if(ans1[i]==0)
{
printf("0/1\n");
}
else
printf("%lld/%lld\n",ans1[i]/x,ans2[i]/x);
}
return 0;
}
4、
裸的带修改莫队模板,自己打的老是不对,只能用网上性能高的了,先不看这个带修改的了。
。询问的还是l到r中有多少个不同的数加修改。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+50;
int a[MAXN],cnt[MAXN],Ans[MAXN];
struct pnode{ int id,l,r,x1,x2,t; } p[MAXN];
struct qnode{ int x,pre,nxt; } q[MAXN];
int compare(pnode x,pnode y)
{ return (x.x1<y.x1)||(x.x1==y.x1&&x.x2<y.x2)||(x.x1==y.x1&&x.x2==y.x2&&x.t<y.t); }
inline int read()
{
int f=1,x=0; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
inline char get_ch()
{
char c=getchar();
while (c<'A'||c>'Z') c=getchar();
return c;
}
int main()
{
int n=read(),m=read(),numq=0,nump=0;
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=m;i++)
{
char c=get_ch();
int x=read(),y=read();
if (c=='Q') p[++nump]=(pnode){nump,x,y,0,0,numq};
else
{
q[++numq]=(qnode){x,a[x],y};
a[x]=y;
}
}
int Size=ceil(exp((log(n)+log(numq))/3));
for (int i=1;i<=nump;i++) p[i].x1=(p[i].l-1)/Size+1,p[i].x2=(p[i].r-1)/Size+1;
sort(p+1,p+nump+1,compare);
int L=1,R=0,T=numq,num=0;
memset(cnt,0,sizeof cnt);
for (int i=1;i<=nump;i++)
{
while (L<p[i].l) cnt[a[L]]--,num-=(cnt[a[L]]==0),L++;
while (L>p[i].l) L--,cnt[a[L]]++,num+=(cnt[a[L]]==1);
while (R<p[i].r) R++,cnt[a[R]]++,num+=(cnt[a[R]]==1);
while (R>p[i].r) cnt[a[R]]--,num-=(cnt[a[R]]==0),R--;
while (T>p[i].t)
{
qnode Q=q[T];
if (p[i].l<=Q.x&&Q.x<=p[i].r)
{
cnt[Q.nxt]--; num-=(cnt[Q.nxt]==0);
cnt[Q.pre]++; num+=(cnt[Q.pre]==1);
}
a[Q.x]=Q.pre;
T--;
}
while (T<p[i].t)
{
T++;
qnode Q=q[T];
if (p[i].l<=Q.x&&Q.x<=p[i].r)
{
cnt[Q.pre]--; num-=(cnt[Q.pre]==0);
cnt[Q.nxt]++; num+=(cnt[Q.nxt]==1);
}
a[Q.x]=Q.nxt;
}
Ans[p[i].id]=num;
}
for (int i=1;i<=nump;i++) printf("%d\n",Ans[i]);
return 0;
}
5.题意:NPY有n个女朋友,编号从1到n,分布在各个班级中,给出m个区间[Li,Ri],每次NPY都想找区间[Li,Ri]中的女朋友约会,要求求出班级排列不同的约会的方案有几种(如果两个女朋友在同一个班,那么交换约会顺序的话还是算一种方案)
先考虑暴力求解的状况:
假设在区间[L,R]之间,cnt[1],cnt[2],cnt[3]…cnt[i]…cnt[maxn]分别表示区间[L,R]之间在班级 i 中的女朋友的数量,那么此时方案数量是:
现在简单推导一下区间改变了之后对方案数的影响:
如果区间改变后一个值的出现次数增加了一次:
最终方案数会改变成ANS*(R-L+1+1)/(cnt[i]+1)
而如果其中一个值出现次数减少了一次
方案数变成ANS*cnt[i]/(R-L+1)
(ANS为已知区间的方案数)
由于1000000007是个大质数,所以对于除法的取模采取乘逆元的办法
先根据费马小定理预处理出1~30000之间所有数对1000000007的逆元,然后莫队分块计算就好了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
const int maxn = 3e4+7;
int block;
struct QUERY{
int lt,rt,id;
bool operator < (const QUERY &x){
if(lt/block==x.lt/block) return rt<x.rt;
else return lt<x.lt;
}
}query[maxn];
int T,n,m,seq[maxn];
LL inv[maxn],res[maxn],ans,cnt[maxn];
LL qpow_mod(LL a,LL b){
LL tmp = 1;
while(b){
if(b&1) tmp = tmp*a%mod;
b>>=1;
a = a*a%mod;
}
return tmp;
}
void inv_table(){
for(int i=1;i<maxn;i++) inv[i] = qpow_mod(i,mod-2);
}
inline void add(LL d,LL x){
ans = ans*(d+1)%mod*inv[++cnt[x]]%mod;
}
inline void sub(LL d,LL x){
ans = ans*(cnt[x]--)%mod*inv[d]%mod;
}
void solve(){
int l=1,r=0;
ans = 1;
for(int i=1;i<=m;i++){
while(query[i].lt<l){
LL d = r-l+1;
add(d,seq[--l]);
}
while(query[i].rt>r){
LL d = r-l+1;
add(d,seq[++r]);
}
while(query[i].lt>l){
LL d = r-l+1;
sub(d,seq[l++]);
}
while(query[i].rt<r){
LL d = r-l+1;
sub(d,seq[r--]);
}
res[query[i].id] = ans;
}
}
int main(){
inv_table();
scanf("%d",&T);
while(T--){
memset(cnt,0,sizeof(cnt));
scanf("%d %d",&n,&m);
block = sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&seq[i]);
for(int i=1;i<=m;i++){
scanf("%d %d",&query[i].lt,&query[i].rt);
query[i].id = i;
}
sort(query+1,query+1+m);
solve();
for(int i=1;i<=m;i++) printf("%Ild\n",res[i]);
}
return 0;
}