莫队问题整理

普通莫队

普通莫队的时间复杂度分析

不同题目,不同分块大小len的分析

奇偶排序trick

奇数块内右端点按照从小到大的顺序,

偶数块内右端点按照从大到小的顺序。 试着画一个山的图差不多意会一下就ok了

单点修改莫队(增加时间维度)

带修莫队时间复杂度分析,块大小分析

回滚莫队

树上莫队 dfs序,分类讨论u,v关系,lca应用

树上带修莫队

树链剖分

二次离线莫队

ProblemP2709 小B的询问


莫队基础题目 莫队基础题目 莫队基础题目

莫队实现了对查询进行分块,好像只能是离线查询,先收集好所有的询问, 维护一个查询闭区间 [ l , r ] , 通过左移、右移待查询的区间,暴力求解,如果 只是一个一个的移动 O ( m ∗ n ) ,不合理,将所有询问分块,不在同一块的按照所在 块的编号排序否则按照块内的右端点排序 , ( 如果是按左端点会 T L E ) 对于不同的题目维护不同的 a d d 和 s u b 函数 . 莫队实现了对查询进行分块,好像只能是离线查询,先收集好所有的询问,\\ 维护一个查询闭区间[l,r],通过左移、右移待查询的区间,暴力求解,如果\\ 只是一个一个的移动O(m*n),不合理,将所有询问分块,不在同一块的按照所在\\ 块的编号排序否则按照块内的右端点排序,(如果是按左端点会TLE)\\ 对于不同的题目维护不同的add和sub函数. 莫队实现了对查询进行分块,好像只能是离线查询,先收集好所有的询问,维护一个查询闭区间[l,r],通过左移、右移待查询的区间,暴力求解,如果只是一个一个的移动O(mn),不合理,将所有询问分块,不在同一块的按照所在块的编号排序否则按照块内的右端点排序,(如果是按左端点会TLE)对于不同的题目维护不同的addsub函数.

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;


typedef long long ll;
const int N=2e5+10;
int n,m,k,_;
int a[N];
int pos[N],cnt,len;//i点所在的块是多少
int c[N];//表示[l,r]中每个a[i]出现的次数
ll res;
ll ans[N];//第i个询问的答案是多少
struct Q
{
	int l,r,idx;
}q[N];

void add(int i)
{
	c[a[i]]++;
	ll x=(c[a[i]]-1)*(c[a[i]]-1);//老的贡献
	ll y=c[a[i]]*c[a[i]];//新的贡献
	res+=y-x;
}

void sub(int i)
{
	c[a[i]]--;
	ll x=(c[a[i]]+1)*(c[a[i]]+1);
	ll y=c[a[i]]*c[a[i]];
	res-=x-y;
}

int main()
{
	cin>>n>>m>>k;
	len=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		pos[i]=i/len;
	}
	for(int i=0;i<m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].idx=i;
	}
	
	sort(q,q+m,[](Q x,Q y){
		return pos[x.l]==pos[y.l]?x.r<y.r:x.l<y.l; //两种写法都能A
        //return pos[x.l]==pos[y.l]?x.r<y.r:pos[x.l]<pos[y.l];
	});
	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].idx]=res;
	}
	
	for(int i=0;i<m;i++)
	{
		printf("%lld\n",ans[i]);
	}
		
	return 0;
}

ProblemSP10707树上计数2

/*Love coding and thinking!*/ 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo2(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
const int N=4e4+10;
int h[N],e[N],ne[N],idx,w[N];
vector<int>num;//没有注意到个点权值均在int范围内,因为有N个点,需要离散化
int dfn[N*2],fir[N],last[N];
// 莫队  u第一次出现的下标,u最后一次出现的下标
bool st[N];//add和dfs用了两次st,服了,以后都用fa替代

int n,cnt,m,_,len;
int pos[N];
int ans[10010];
int res;

struct Q
{
	int l,r,k;
	int p;//如果询问的l,r不存在祖宗关系
}q[10010];

int Cnt[N];
//lca需要用到的数组
int fa[N][16],depth[N];

void Add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs(int u,int fa)
{
	dfn[++cnt]=u;
	fir[u]=cnt;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j!=fa)	dfs(j,u);
	}
	dfn[++cnt]=u;
	last[u]=cnt;
}

int lca(int a,int b)
{
	if(depth[a]<depth[b])
		swap(a,b);
	for(int k=15;k>=0;k--)
		if(depth[fa[a][k]]>=depth[b])
			a=fa[a][k];
	if(a==b)return a;
	for(int k=15;k>=0;k--)
	{
		if(fa[a][k]!=fa[b][k])
		{
			a=fa[a][k],b=fa[b][k];
		}
	}
	return fa[a][0];
}

void bfs(int u)//bfs求树深度,更新f数组预处理O(nlogn)
{
    memset(depth,0x3f,sizeof depth);
    queue<int>que;
    depth[0]=0;//必要的哨兵
    depth[1]=1;
    que.push(u);
    while(que.size())
    {
        int t=que.front();
        que.pop();
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(depth[j]>depth[t]+1)
            {
                depth[j]=depth[t]+1;
                que.push(j);
                fa[j][0]=t;
                for(int i=1;i<=15;i++)
                    fa[j][i]=fa[fa[j][i-1]][i-1];
            }
        }
    }
}

void add(int x)//
{
	st[x]^=1;
	if(st[x]==0)
	{
		Cnt[w[x]]--;
		if(!Cnt[w[x]])res--;
	}
	else
	{
		if(!Cnt[w[x]])res++;
		Cnt[w[x]]++;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++){scanf("%d",&w[i]);num.pb(w[i]);}
	sort(num.begin(),num.end());
	num.erase(unique(num.begin(),num.end()),num.end());
	for(int i=1;i<=n;i++)
		w[i]=lower_bound(num.begin(),num.end(),w[i])-num.begin();
	
	mem(h,-1);
	
	for(int i=0;i<n-1;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		Add(u,v);
		Add(v,u);//忘了双向了
	}
	dfs(1,-1);
	bfs(1);
	// for(int i=1;i<=2*n;i++)
		// cout<<dfn[i]<<endl;
	len=sqrt(n*2);
	for(int i=1;i<=2*n;i++)
	{
		pos[i]=i/len;
	}
	//读入的时候写的不对,因为要设计lca特判,所以需要在读入之前进行处理
	// for(int i=0;i<m;i++)
	// {
		// scanf("%d%d",&q[i].l,&q[i].r);
		// q[i].idx=i;
	// }
	
	for(int i=0;i<m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		if(fir[a]>fir[b])swap(a,b);
		int p=lca(a,b);
		if(a==p)
			q[i]={fir[a],fir[b],i};
		else
			q[i]={last[a],fir[b],i,p};//需要把最近公共祖先加上去
	}
	
	// sort(q,q+m,[](Q x,Q y){
		// return pos[x.l]==pos[y.l]?x.r<y.r:pos[x.l]<pos[y.l];
	// });
	
	sort(q,q+m,[](Q x,Q y){//树上莫队还奇偶性排序嘛?
		return pos[x.l]==pos[y.l]?(x.k&1?x.r<y.r:x.r>y.r):pos[x.l]<pos[y.l];
	});
	int l=1,r=0;
	for(int i=0;i<m;i++)
	{
		 while(q[i].l<l)add(dfn[--l]);//在括号序中莫队
		 while(q[i].r>r)add(dfn[++r]);
		 while(q[i].l>l)add(dfn[l++]);
		 while(q[i].r<r)add(dfn[r--]);
		 
		 if(q[i].p) add(q[i].p);
		 ans[q[i].k]=res;
 		 if(q[i].p) add(q[i].p);//非常重要!如果有lca的话,因为[l,r]中没有这个点,一定要删去
	}
	for(int i=0;i<m;i++)
		printf("%d\n",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值