lyw姐姐放假回来给我们讲课
比zgs好
莫队基础
分块降时间复杂度
把区间之间的转化优化为求一个曼哈顿距离
(
∣
l
1
−
l
2
∣
+
∣
r
1
−
r
2
∣
)
(|l_1-l_2|+|r_1-r_2|)
(∣l1−l2∣+∣r1−r2∣)
再优化这个曼哈顿距离——利用分块
即以L所在的块为第一关键字,R第二关键字排序
莫队要求离线
HH的项链
code
//莫队过不去
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
const int LEN=1e6+10;
int n,m,size;
int line[LEN];
struct node{
int l,r,i,p,ans;
}quary[LEN];
int cnt[LEN];
int ans;
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
inline bool comp(const node &a,const node &b){
if(a.p!=b.p)return a.p<b.p;
return a.r<b.r;
}
inline bool comp_for_back(const node &a,const node &b){
return a.i<b.i;
}
inline void Write(const int &a){
int x=a;
if(x<0){
putchar('-');
x=-x;
}
static short buf[20];
short tot=0;
do{
buf[tot++]=x%10;
x/=10;
}while(x);
while(tot)putchar(buf[--tot]+'0');
}
int main(){
n=in;
size=(int)sqrt(n);
for(re int i=1;i<=n;i++)line[i]=in;
m=in;
for(re int i=1;i<=m;i++){
quary[i].l=in;
quary[i].r=in;
quary[i].i=i;
quary[i].p=(quary[i].l-1)/size+1;
}
sort(quary+1,quary+m+1,comp);
ans=0;
int l=1,r=0;
for(re int i=1;i<=m;i++){
while(r<quary[i].r){
r++;
if(!cnt[line[r]])ans++;
cnt[line[r]]++;
}
while(r>quary[i].r){
cnt[line[r]]--;
if(!cnt[line[r]])ans--;
r--;
}
while(l<quary[i].l){
cnt[line[l]]--;
if(!cnt[line[l]])ans--;
l++;
}
while(l>quary[i].l){
l--;
if(!cnt[line[l]])ans++;
cnt[line[l]]++;
}
quary[i].ans=ans;
}
sort(quary+1,quary+m+1,comp_for_back);
for(re int i=1;i<=m;i++)Write(quary[i].ans),putchar('\n');
return 0;
}
别去络谷,会T(用线段树就行了,可惜我还没把它搞熟嘤嘤嘤~)
练习:BZOJ 2038 3781
(提高)3289 3809 4540
BZOJ2038
莫队需要特别注意的点在于移动l,r指针的时候各种量之间的关系:加减关系,包含关系等
#include<bits/stdc++.h>
#define in Read()
#define re register
#define int long long
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=5e4+10;
int n,m,c[NNN],size;
int r,l,cnt[NNN];
struct fraction{
int nume,deno;
};
struct node{
int l,r,i,p;
fraction ans;
}query[NNN];
inline int gcd(int x,int y){
if(x%y==0)return y;
return gcd(y,x%y);
}
inline fraction frac(int nume/*分子*/,int deno/*分母:输进来r-l+1*/){
fraction ans;
//特判
if(!nume){
ans.nume=0,ans.deno=1;
return ans;
}
//把分母调对
if(deno%2)deno=(deno-1)/2*deno;
else deno=(deno/2)*(deno-1);
int div=gcd(nume,deno);
nume=nume/div;
deno=deno/div;
ans.nume=nume;
ans.deno=deno;
return ans;
}
inline bool comp(const node &a,const node &b){
if(a.p!=b.p)return a.p<b.p;
return a.r<b.r;
}
inline bool back(const node &a,const node &b){
return a.i<b.i;
}
signed main(){
n=in,m=in;
size=(int)sqrt(n);
for(re int i=1;i<=n;++i)c[i]=in;
for(re int i=1;i<=m;++i){
query[i].l=in;
query[i].r=in;
query[i].i=i;
query[i].p=(query[i].l-1)/size;
}
std::sort(query+1,query+m+1,comp);
int ans=0;
l=1,r=0;
for(re int i=1;i<=m;++i){
while(r<query[i].r){
++r;
ans+=cnt[c[r]];
++cnt[c[r]];
}
while(r>query[i].r){
--cnt[c[r]];
ans-=cnt[c[r]];
--r;
}
while(l>query[i].l){
--l;
ans+=cnt[c[l]];
++cnt[c[l]];
}
while(l<query[i].l){
--cnt[c[l]];
ans-=cnt[c[l]];
++l;
}
query[i].ans=frac(ans,query[i].r-query[i].l+1);
}
std::sort(query+1,query+m+1,back);
for(re int i=1;i<=m;++i)
printf("%lld/%lld\n",query[i].ans.nume,query[i].ans.deno);
return 0;
}
BZOJ3781
(很明显BZOJ并不想让你做这道题,那么全能的络谷就可以出马了)
这道题非常尴尬
我又在它上面浪费了1小时
在调试的时候遇到了一个非常日龙的情况:

然后

大草
最后发现MLE了
code
#include<bits/stdc++.h>
#define in Read()
#define re register
#define int long long
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=5e4+10;
int n,m,k;
int a[NNN],cnt[NNN];
int l=1,r;
int ans;
struct node{
int l,r,i,p;
int ans;
}query[NNN];
inline int sqare(int x){return x*x;}
inline void INIT(){
n=in,m=in,k=in;
int size=(int)std::sqrt(n);
for(re int i=1;i<=n;++i)a[i]=in;
for(re int i=1;i<=m;++i){
query[i].l=in;
query[i].r=in;
query[i].i=i;
query[i].p=(query[i].l-1)/size;
}
}
inline bool comp(const node &a,const node &b){return (a.p!=b.p)?a.p<b.p:a.r<b.r;}
inline bool back(const node &a,const node &b){return a.i<b.i;}
inline void solve(){
for(re int i=1;i<=m;++i){
while(r<query[i].r){
++r;
ans+=2*cnt[a[r]]+1;
++cnt[a[r]];
}
while(r>query[i].r){
ans-=2*cnt[a[r]]-1;
--cnt[a[r]];
--r;
}
while(l<query[i].l){
ans-=2*cnt[a[l]]-1;
--cnt[a[l]];
++l;
}
while(l>query[i].l){
--l;
ans+=2*cnt[a[l]]+1;
++cnt[a[l]];
}
query[i].ans=ans;
}
}
signed main(){
INIT();
std::sort(query+1,query+m+1,comp);
solve();
std::sort(query+1,query+m+1,back);
for(re int i=1;i<=m;++i){
printf("%lld\n",query[i].ans);
}
return 0;
}
BZOJ3289
莫队+树状数组求逆序对
(求逆序对:冒泡O(N2),归并O(NlogN+代码难giao),树状数组O(NlogN))
#include<bits/stdc++.h>
#define in Read()
#define re register
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=5e4+10;
int a[NNN],block;
struct node{
int l,r,i,ans,p;
inline void get_query(int _i){
l=in,r=in,i=_i,p=(l-1)/block;//细节
}
}query[NNN];
int tree[NNN];
int n,q;
int disc[NNN];
inline bool comp(const node &u,const node &v){
return (u.p!=v.p)?u.p<v.p:u.r<v.r;
}
inline bool back(const node &u,const node &v){
return u.i<v.i;
}
inline int lowbit(int i){
return i&(-i);
}
inline void Add(int x,int pos){
while(x<=n){
tree[x]+=pos;
x+=lowbit(x);
}
}
inline int Query(int x){
unsigned int sum=0;
while(x>0){
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
inline void SOLVE(){
int l=1,r=0,ans=0;
std::sort(query+1,query+q+1,comp);
for(re int i=1;i<=q;++i){
while(r<query[i].r){
++r;
Add(a[r],1);
ans+=r-l+1-Query(a[r]);
}
while(r>query[i].r){
Add(a[r],-1);
ans-=r-l-Query(a[r]);
--r;
}
while(l<query[i].l){
Add(a[l],-1);
ans-=Query(a[l]-1);
++l;
}
while(l>query[i].l){
--l;
Add(a[l],1);
ans+=Query(a[l]-1);
}
query[i].ans=ans;
}
std::sort(query+1,query+q+1,back);
}
int main(){
n=in;
block=(int)std::sqrt(n);
for(re int i=1;i<=n;++i)disc[i]=a[i]=in;
std::sort(disc+1,disc+n+1);
for(re int i=1;i<=n;++i)a[i]=std::lower_bound(disc+1,disc+n+1,a[i])-disc;
q=in;
for(re int i=1;i<=q;++i)query[i].get_query(i);
SOLVE();
for(re int i=1;i<=q;++i)printf("%d\n",query[i].ans);
return 0;
}
解释一下移动左右指针的地方
树状数组,Query(x)返回的是x到0所有点的和
每个点为权值表示的话,返回的就是存在于数组的、小于x的数字个数
每次改变一个点,就要计算生成或消失的逆序对个数
对于一个按照下标依次+1来加入的序列它的逆序对个数N要这样算:
Add
ans+=i-Query(i)
把这个结论应用到本题上:
每移动一个指针,加入或者消除树状数组中该点,然后计算
- r++
元素入树
总共r-l+1个数,比a[r]小的有Query个(可以算自己:最后r-l+1加的那个1是可以把加进去的自己去掉的,后面l是另一种算法,没有算自己的,因为不好确定l是否在树状数组里面)
新增了r-l+1-Query个逆序对 - r–
元素出树
总共r-l个数(r–把出树的去掉了),比a[r]小的有Query个
减少了r-l-Query个逆序对 - l++
元素出树
总共r-l个数,比a[l]小的有Query(a[l]-1)个(前缀和思想,不要吧自己包含进去了)
减少了Query(a[l]-1)个 - l–
元素入树
总共r-l个数,比a[l]小的有Query(a[l]-1)个(前缀和思想)
增加了Query(a[l]-1)个
注意再加点的时候加的是文件大小(a数组)

437

被折叠的 条评论
为什么被折叠?



