首页 > 其他分享 >[Ynoi2016] 这是我自己的发明(根号分治+分块/莫队)

[Ynoi2016] 这是我自己的发明(根号分治+分块/莫队)

时间:2023-08-03 21:56:46浏览次数:42  
标签:return int ++ Ynoi2016 jump dep mk 莫队 根号

题目传送门

soltion

简单题

换根显然可以拆成 \(O(1)\) 个区间,这里先不管。

直接做法是莫队,把双子树拆成 \(dfs\) 序上的双前缀,可以直接莫队,但是常数比较大。

另一种做法是根分,对颜色出现次数分治,大于的求出 \(dfs\) 序的前缀和即可,小于的因为一共只有 \(O(n\sqrt n)\) 个点对,所以变成二维数点扫描线做,用分块平衡一下即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
const int M = 5e5+7;
int n,m;
struct edge 
{
	int y,next;
}e[2*N];
int flink[N],t=0;
void add(int x,int y)
{
	e[++t].y=y;
	e[t].next=flink[x];
	flink[x]=t;
}
int st[N],ed[N],seq[N],tot=0;
int jump[N][20],dep[N];
void dfs(int x,int pre)
{
	dep[x]=dep[pre]+1;
	seq[st[x]=++tot]=x;
	jump[x][0]=pre;
	for(int k=1;jump[x][k-1];k++)jump[x][k]=jump[jump[x][k-1]][k-1];
	for(int i=flink[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==pre)continue;
		dfs(y,x); 
	}
	ed[x]=tot;
}
int find(int x,int y)
{
	for(int k=19;k>=0;k--)
	if(dep[jump[x][k]]>dep[y])x=jump[x][k];
	return x;
}
bool Anc(int x,int y)
{
	return st[x]<=st[y]&&st[y]<=ed[x];
}
int a[N],dct[N],num=0;
int B;
vector<int> pos[N];
#define PII pair<int,int>
#define mk(x,y) make_pair(x,y)
#define X(x) x.first
#define Y(x) x.second
vector<PII> SubTree(int r,int x)
{
	if(!Anc(x,r)) return {mk(st[x],ed[x])};
	if(r==x) return {mk(1,n)};
	int y=find(r,x);
	return {mk(1,st[y]-1),mk(ed[y]+1,n)};
}
typedef long long LL;
LL Ans[M];
int op[M];
vector<PII> Sx[M],Sy[M];
int s[N];
inline int query(int l,int r){return s[r]-s[l-1];}
struct Query 
{
	int id,l,r,v;
};
vector<Query> Q[N];  
inline void Apply(int u,int l,int r,int L,int R)
{
	if(l>r||L>R)return;
	Q[l-1].push_back((Query){u,L,R,-1});
	Q[r].push_back((Query){u,L,R,1});
}
int sum[N],w[N],bel[N],L[N],R[N];
inline void upd(int x)
{
	//printf("upd(%d)\n",x);
	w[x]++;
	sum[bel[x]]++;
}
int ask(int l,int r)
{
	//printf("ask(%d %d)\n",l,r);
	int res=0;
	if(bel[l]==bel[r])
	{
		for(int i=l;i<=r;i++)res+=w[i];
		return res;
	}
	for(int i=l;i<=R[bel[l]];i++)res+=w[i];
	for(int i=L[bel[r]];i<=r;i++)res+=w[i];
	for(int i=bel[l]+1;i<bel[r];i++)res+=sum[i];
	return res;
}
int main()
{
	cin>>n>>m;B=sqrt(2*n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),dct[++num]=a[i];
	sort(dct+1,dct+num+1);
	num=unique(dct+1,dct+num+1)-dct-1;
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(dct+1,dct+num+1,a[i])-dct;
		pos[a[i]].push_back(i);
	}
	for(int i=2;i<=n;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	int r=1;
//	for(int i=1;i<=n;i++)
//	cout<<seq[i]<<endl;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d",&op[i]);
		if(op[i]==1)
		{
			scanf("%d",&x);
			r=x;
		}
		else 
		{
			scanf("%d %d",&x,&y);
			Sx[i]=SubTree(r,x);
			Sy[i]=SubTree(r,y);
			for(auto u:Sx[i])
			for(auto v:Sy[i])
			Apply(i,X(u),Y(u),X(v),Y(v));
		}
	}
	for(int c=1;c<=num;c++)if(pos[c].size()>B)
	{
		for(int i=1;i<=n;i++)s[i]=s[i-1]+(a[seq[i]]==c);
		for(int i=1;i<=m;i++)
		{
			if(op[i]==2)
			{
				for(auto x:Sx[i])
				for(auto y:Sy[i])
				Ans[i]+=1ll*query(X(x),Y(x))*query(X(y),Y(y));
			}
		}
	}
	int O=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		bel[i]=(i-1)/O+1;
		R[bel[i]]=i;
		if(!L[bel[i]])L[bel[i]]=i; 
	}
	for(int i=1;i<=n;i++)
	{
		if(pos[a[seq[i]]].size()<=B)for(int x:pos[a[seq[i]]])upd(st[x]);
		for(auto U:Q[i]) Ans[U.id]+=1ll*U.v*ask(U.l,U.r);
	}
	for(int i=1;i<=m;i++)
	if(op[i]==2)printf("%lld\n",Ans[i]);
	return 0;
}

标签:return,int,++,Ynoi2016,jump,dep,mk,莫队,根号
From: https://www.cnblogs.com/jesoyizexry/p/17604579.html

相关文章

  • 【230803-3】三角形ABC中,角ABC的对边分别是abc。若a=根号5/2*b,A=2B。则CosB=?
    ......
  • 【230803-1】在三角形ABC中,角A,B,C所对的边为a,b,c,若b/(根号3*CosB)=a/SinA,则CosB=?
    ......
  • 【230731-2】三角形ABC的内角A,B,C的对边分别为a,b,c,已知SinB+SinA(SinC-CosC)=0,a=2,
    ......
  • 普通莫队
    普通莫队形式如果从\([l,r]\)的答案能够$O(1)$扩展到\([l+1,r][l-1,r][l,r+1][l,r-1]\)(即与\([l,r]\)相邻的区间)的答案,那么使用莫队算法可以在\(O(n\sqrtn)\)的复杂度内求出所有询问的答案。核心我们假设已经知道\([l,r]\)的答案,现在我们要求\([l',r']\),我们就可以移动左右指针......
  • 根号 n 算法
    分块动态单点修改单点修改$O(\sqrt{n})$,区间查询$O(1)$动态区间修改加懒惰标记......
  • 莫队学习
    大致思路:1.分块2.对提问排序3.暴力处理#莫队模板```cpp#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+10,M=1e6+10;inta[N],belong[N],bnum,cnt[N];intn,q,len,ans[M];structnode{ intl,r,id;}e[M];boolcmp(nodea,nodeb){ return(belong[a.l]^belong[b......
  • 莫队
    简介用于解决一类离线区间询问问题。将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。普通莫队算法概述对于序列上的区间询问问题,如果从\([l,r]\)答案能够\(O(1)\)扩展到\([l-1,r],[l+1,r],[l,r+1],[l,r-1]\)(即与\([l,r]\)相邻的区间)的答案,那么......
  • 莫队学习笔记
    这是一篇模仿算导的学习笔记/题解。例题:P1494给定一个长为\(n\)的数组\(a\)和\(m\)个询问(有序数对)\(b_i=(l_i,r_i)\),询问允许离线,对每个询问\((l,r)\)求出满足\(l\lei\ltj\ltr\wedgea_i=a_j\)的数对\((i,j)\)数量.证明:若数\(x\)在\(a\)数组下标为......
  • 莫队学习笔记
    引入问题给出一个长度为\(n\)的数组\(A\),接下来\(q\)次询问,每次询问求\([l,r]\)中有多少组\((i,j,k)\)使得\(a_i=a_j=a_k(i<j<k)\)。保证\(1\leqn\leq10^5,1\leqA_i\leqn(1\leqi\leqn)\)。莫队的基础思想——区间转移简单分析问题,貌似并没有可加性,所以分块和......
  • 莫队 学习笔记
    莫队学习笔记引入莫队算法是一种优美的暴力算法,可以用来处理大量的区间询问。前提是,维护的信息可以高效地插入、删除。我们就以一道题为例,来初探莫队:洛谷P3901数列找不同题意:给定一个数列,\(q\)次询问,问区间\([l,r]\)内的数是否各不相同。首先,我们很容易想到,问某个区间......