ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

大厨的分块题

2021-08-14 21:03:00  阅读:170  来源: 互联网

标签:10 const 分块 int -- maxn include 大厨


目录

\(\text{Chef and Churu}\)

解法

这道题显然有两种信息维护方式:

  • 维护 \(A\) 的前缀和,对于单个函数 \(f(i)\),直接查询对应的 \(A\)。
  • 对于 \(A_i\) 的修改,修改区间包含 \(i\) 的函数 \(f(i)\)。

第二种维护可以将函数分块,维护每个块内所有 \(A_i\) 的贡献次数,差分实现可以做到 \(\mathcal O(n\cdot \frac{n}{B})\)(具体实现请见 getNum() 函数)预处理。但无法处理散块的情况,而散块满足大小在 \(B\) 范围,所以可以暴力更新函数。

如何维护 \(A\) 的前缀和?可以用数据结构进行维护,但询问散块复杂度就上升到 \(\mathcal O(qB\cdot \log n)\)。其实只需要维护每块的前缀和和块与块之间的前缀和,这样单次修改是是 \(\mathcal O(B+\frac{n}{B})\) 的,询问降到了 \(\mathcal O(qB)\)。总体复杂度是 \(\mathcal O(q\cdot(B+\frac{n}{B}))\),所以 \(B\) 取 \(\sqrt n\) 即可。

需要注意的是,答案范围 \(10^5\times 10^5\times 10^9=10^{19}\),要开 unsigned long long

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
} 

#include <cmath>
#include <iostream>
using namespace std;
typedef unsigned long long ll;

const int maxn=1e5+5;

int n,a[maxn],bl[maxn];
int rk[maxn],cnt,B,L[maxn];
int R[maxn];
struct Block {
	int l,r,num[maxn];
	ll sum[400],S,ans;
} s[400];

void getSum(int x) {
	ll tmp=0;
	for(int i=s[x].l;i<=s[x].r;++i) {
		tmp+=a[i];
		s[x].sum[rk[i]]=tmp;
	}
	s[x].sum[B]=tmp;
	for(int i=x;i<=cnt;++i)	
		s[i].S=s[i-1].S+s[i].sum[B];
}

ll Query(int x,int y) {
	if(bl[x]^bl[y]) {
		if(bl[x]+1==bl[y])
			return s[bl[y]].sum[rk[y]]-s[bl[x]].sum[rk[x]-1]+s[bl[x]].sum[B];
		return s[bl[y]].sum[rk[y]]+s[bl[y]-1].S-s[bl[x]-1].S-s[bl[x]].sum[rk[x]-1];
	}
	return s[bl[x]].sum[rk[y]]-s[bl[x]].sum[rk[x]-1];
}

void getNum(int x) {
	for(int i=s[x].l;i<=s[x].r;++i) {
		++s[x].num[L[i]],--s[x].num[R[i]+1];
		s[x].ans+=Query(L[i],R[i]);
	}
	for(int i=1;i<=n;++i)
		s[x].num[i]+=s[x].num[i-1];
}

int main() {
	n=read(9); B=sqrt(n);
	for(int i=1;i<=n;++i)
		a[i]=read(9);
	for(int i=1;i<=n;++i) {
		L[i]=read(9),R[i]=read(9);
		bl[i]=(i-1)/B+1;
		rk[i]=(i-1)%B+1;
	}
	cnt=bl[n];
	for(int i=1;i<=cnt;++i) {
		s[i].l=s[i-1].r+1;
		s[i].r=s[i].l+B-1;
	}
	s[cnt].r=min(s[cnt].r,n);
	for(int i=1;i<=cnt;++i)
		getSum(i);
	for(int i=1;i<=cnt;++i)
		getNum(i);
	int op,x; ll y,dec,Ans;
	for(int m=read(9);m;--m) {
		op=read(9),x=read(9),y=read(9);
		if(op==1) {
			dec=y-a[x],a[x]=y;
			getSum(bl[x]);
			for(int i=1;i<=cnt;++i)
				s[i].ans+=dec*s[i].num[x];
		}
		else {
			Ans=0;
			if(bl[x]^bl[y]) {
				for(int i=x;i<=s[bl[x]].r;++i)
					Ans+=Query(L[i],R[i]);
				for(int i=s[bl[y]].l;i<=y;++i)
					Ans+=Query(L[i],R[i]);
				for(int i=bl[x]+1;i<bl[y];++i)
					Ans+=s[i].ans;
			}
			else {
				for(int i=x;i<=y;++i)
					Ans+=Query(L[i],R[i]);
			}
			printf("%llu\n",Ans);
		}
	}
	return 0;
}

\(\text{Chef and Problems}\)

解法

好妙啊,预处理 \(pre_{i,j},suf_{i,j}\) 表示第 \(i\) 种数在 \(j\) 块之后/之前(包括第 \(j\) 块)的最早/最晚出现位置。根据这个可以 \(\mathtt{dp}\) 出 \(f_{i,j}\) 表示 \(i,j\) 块之间的答案。

询问时讨论 \(l,r\) 分别是否在 \([bl_l+1,bl_r-1]\) 之间即可。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
} 

#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;

const int maxn=1e5+5,inf=1e9;

int n,m,q,a[maxn],bl[maxn],B;
int pre[maxn][400],suf[maxn][400];
int f[400][400],Left[maxn];

int main() {
	memset(pre,0x3f,sizeof pre);
	n=read(9),m=read(9),q=read(9);
	B=sqrt(n); 
	for(int i=1;i<=n;++i)
		a[i]=read(9),
		bl[i]=(i-1)/B+1,
		suf[a[i]][bl[i]]=i;
	for(int i=n;i>=1;--i)
		pre[a[i]][bl[i]]=i;
	for(int i=1;i<=m;++i) {
		for(int j=bl[n];j>=1;--j)
			pre[a[i]][j]=min(pre[a[i]][j],pre[a[i]][j+1]);
		for(int j=1;j<=bl[n];++j)
			suf[a[i]][j]=max(suf[a[i]][j],suf[a[i]][j-1]);
	}
	for(int i=bl[n];i>=1;--i)
		for(int j=i;j<=bl[n];++j) {
			f[i][j]=max(f[i+1][j],f[i][j-1]);
			for(int k=(i-1)*B+1;k<=min(i*B,n);++k)
				f[i][j]=max(f[i][j],suf[a[k]][j]-k);
		}
	int l,r,ans;
	while(q--) {
		l=read(9),r=read(9);
		ans=f[bl[l]+1][bl[r]-1];
		for(int i=l;i<=min(bl[l]*B,n);++i)
			ans=max(ans,suf[a[i]][bl[r]-1]-i);
		for(int i=min(bl[l]*B,n);i>=l;--i)
			Left[a[i]]=i;
		for(int i=(bl[r]-1)*B;i<=r;++i) {
			ans=max(ans,i-pre[a[i]][bl[l]+1]);
			if(Left[a[i]])
				ans=max(ans,i-Left[a[i]]);
		}
		for(int i=min(bl[l]*B,n);i>=l;--i)
			Left[a[i]]=0;
		print(ans,'\n');
	}
	return 0;
}

\(\text{Children Trips}\)

题目大意

给定 \(n\) 个点的树,每条边长度是 \(1\) 或 \(2\)。有 \(m\) 个询问,每次给出 \(x,y,z\),你需要算出 \(x\rightarrow y\) 的路径最少能被划分成多少长度不超过 \(z\) 的段。

\(n,m\le 10^5\)。

解法

考虑计算 \(x,y\) 的 \(\rm lca\),分别跳上去计算答案,最后几步可以拼起来。

分块可以利用 \(z\) 的性质:

  • \(z>\sqrt n\)。如果每次跳 \(z\) 步,那么至多跳 \(\sqrt n\) 次。由此可以直接模拟向上跳的过程,每次花费 \(\mathcal O(\log n)\) 跳至多 \(z\) 步,所以是 \(\mathcal O(m\sqrt n\cdot \log n)\) 的。
  • \(z\le \sqrt n\)。\(z\) 的种数不超过 \(\sqrt n\)。考虑加速向上跳的过程:预处理一个倍增的数组 \(up_{i,j}\),初始化 \(up_{i,0}\) 为 \(i\) 向上跳 \(2^0\) 个 \(z\) 到的点。复杂度 \(\mathcal O(n\sqrt n\cdot \log n+m\log n)\)。

有个需要注意的点就是不能跳到 \(\rm lca\),最多只能跳到它下面。这是为了处理最后几步的情况。

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
} 

#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair <int,int> pii;

const int maxn=1e5+5;

int n,m,head[maxn],cnt,M;
int ans[maxn],s_cnt,b_cnt;
int f[maxn][20],dep[maxn];
int up[maxn][20],dis[maxn];
struct node {
	int x,y,p,id;
	
	bool operator < (const node &t) const {
		return p<t.p;
	}
} S[maxn],B[maxn];
struct Edge {
	int nxt,to,w;
} e[maxn<<1];

void addEdge(int u,int v,int w) {
	e[++cnt].w=w;
	e[cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
	e[++cnt].w=w;
	e[cnt].to=u;
	e[cnt].nxt=head[v];
	head[v]=cnt;
}

int Lca(int x,int y) {
	if(dep[x]<dep[y])	
		swap(x,y);
	for(int i=18;i>=0;--i)
		if(dep[f[x][i]]>=dep[y])
			x=f[x][i];
	if(x==y) return x;
	for(int i=18;i>=0;--i)
		if(f[x][i]^f[y][i]) {
			x=f[x][i];
			y=f[y][i];
		}
	return f[x][0];
}

int jump(int x,int p) {
	int rec=x;
	for(int i=18;i>=0;--i)
		if(dis[rec]-dis[f[x][i]]<=p)
			x=f[x][i];
	return x;
}

pii brute(int x,int y,int p) {
	pii r=make_pair(0,0);
	while(x^y) {
		int z=jump(x,p);
		if(dep[z]>dep[y])	
			++r.first,
			x=z;
		else 
			r.second+=dis[x]-dis[y],x=y;
	}
	return r;
}

pii Jump(int x,int y,int p) {
	pii r=make_pair(0,0);
	for(int i=18;i>=0;--i)
		if(dep[up[x][i]]>dep[y])
			x=up[x][i],
			r.first+=(1<<i);
	r.second=dis[x]-dis[y];
	return r;
}

void init(int p) {
	for(int i=1;i<=n;++i)
		up[i][0]=jump(i,p);
	for(int j=1;j<=18;++j)
		for(int i=1;i<=n;++i)
			up[i][j]=up[up[i][j-1]][j-1];
}

void calc(const node &t) {
	if(t.x==t.y) {
		ans[t.id]=0;
		return;
	}
	int lca=Lca(t.x,t.y);
	pii r1,r2;
	if(t.p>M) {
		r1=brute(t.x,lca,t.p);
		r2=brute(t.y,lca,t.p);
	}
	else {
		r1=Jump(t.x,lca,t.p);
		r2=Jump(t.y,lca,t.p);
	}
	ans[t.id]=r1.first+r2.first;
	if(r1.second+r2.second<=t.p)
		++ans[t.id];
	else ans[t.id]+=2;
}

void b_solve() {
	for(int i=1;i<=b_cnt;++i) 
		calc(B[i]);
}

void s_solve() {
	sort(S+1,S+s_cnt+1);
	for(int i=1;i<=s_cnt;++i) {
		if(i==1 or (S[i].p^S[i-1].p))
			init(S[i].p);
		calc(S[i]);
	}
}

void dfs(int u,int fa) {
	f[u][0]=fa,dep[u]=dep[fa]+1;
	for(int i=1;i<=18;++i)
		f[u][i]=f[f[u][i-1]][i-1];
	for(int i=head[u];i;i=e[i].nxt)
		if(e[i].to^fa)
			dis[e[i].to]=dis[u]+e[i].w,
			dfs(e[i].to,u);
}

int main() {
	n=read(9); M=sqrt(n);
	int u,v,w;
	for(int i=1;i<n;++i) {
		u=read(9),v=read(9),w=read(9);
		addEdge(u,v,w);
	}
	m=read(9); dfs(1,0);
	for(int i=1;i<=m;++i) {
		u=read(9),v=read(9),w=read(9);
		if(w>M) 
			B[++b_cnt]=(node){u,v,w,i};
		else
			S[++s_cnt]=(node){u,v,w,i};
	}
	b_solve(),s_solve();
	for(int i=1;i<=m;++i)
		print(ans[i],'\n');
	return 0;
}

标签:10,const,分块,int,--,maxn,include,大厨
来源: https://www.cnblogs.com/AWhiteWall/p/15139575.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有