首页 > 其他分享 >CF2009G. Yunli's Subarray Queries 题解

CF2009G. Yunli's Subarray Queries 题解

时间:2024-09-07 14:04:28浏览次数:5  
标签:res 200005 int 题解 top st -- Queries Yunli

G1

题目要求,对于一个子区间 $a_{l\sim l+k-1}$,最少要进行多少次单点修改,才能使 $\forall \ l < i \leq l+k-1,a_i=a_{i-1}+1$,其中 $k$ 是固定的。

对于这种问题,我们通常有一个 trick:将 $a_i$ 变为 $a_i-i$。这样的话,我们要求的答案就变为了 $k$ 减去变化后的 $a_{l\sim l+k-1}$ 中众数的出现次数。这个可以用线段树或者莫队做。

以下是线段树的代码,为了避免出现 $a_i < 0$,这里将 $a_i$ 变为了 $a_i-i+n$。

#include<bits/stdc++.h>
#define int long long
using namespace std;

int T,n,m,q,a[200005],ans[200005];

namespace SGT{
	int t[400005<<2];
	inline void pushup(int k){
		t[k]=max(t[k<<1],t[k<<1|1]);return;	
	}
	inline void build(int k=1,int l=1,int r=2*n){
		if(l==r){
			t[k]=0;
		}else{
			int mid=l+r>>1;
			build(k<<1,l,mid),build(k<<1|1,mid+1,r);
			pushup(k);
		}return;
	}
	inline void updata(int P,int val,int k=1,int l=1,int r=2*n){
		if(P<=l&&r<=P){
			t[k]+=val;
		}else{
			int mid=l+r>>1;
			if(P<=mid)updata(P,val,k<<1,l,mid);
			else updata(P,val,k<<1|1,mid+1,r);
			pushup(k);
		}return;
	}
}

inline int read(){
	register int x(0),t(0);
	static char ch=getchar();
	while(!isdigit(ch)){t|=(ch=='-'),ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48),ch=getchar();}
	return t?-x:x;
}

signed main(){
	T=read();while(T--){
	
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++){
		a[i]=read()+n-i;
	}
	SGT::build();
	for(int i=1;i<=m;i++){
		SGT::updata(a[i],1);
	}
	ans[1]=m-SGT::t[1];
	for(int i=2;i+m-1<=n;i++){
		SGT::updata(a[i-1],-1);
		SGT::updata(a[i+m-1],1);
		ans[i]=m-SGT::t[1];
	}
	for(int i=1;i<=q;i++){
		int l=read(),r=read();
		cout<<ans[l]<<'\n';
	}
	
    }return 0;
}

时间复杂度为 $O(n\times\log n)$。

G2

我们先用 G1 的方法求出 $f({a_i,a_{i+1},\dots, a_{i+k-1}})$,并设其为 $w_i$。

与 G1 不同,G2 要求的的是 $\sum \limits_{i=l}^{r-k+1} \min \limits_{j=l}^{i} w_j$,其中 $r \geq l+k-1$。

看到对一段前缀取最小值,我们考虑如何模拟出这一过程。设 $p_0=l$,每次往后找到离 $p_0$ 最近的满足 $w_{p_0} > w_p$ 的 $p$,此时 $\min \limits_{j=l}^{i} w_j=w_{p_0}(p_0\le i\le p - 1)$,这段对答案的贡献为 $(p-p_0)\times w_{p_0}$ ,再将 $p_0$ 设为 $p$。不断这样做下去,直到 $p_0$ 后已经没有 $w_{p_0}>w_p$ 或者 $p_0>r-k+1$。

我们不可能每次询问都这样暴力做一次,考虑如何优化。显然,对于每个 $p_0$,其后最近的满足 $w_{p_0} > w_p$ 的 $p$ 都是不变的。考虑提前将每个 $p_0$ 的 $p$ 求出来,这个可以用单调栈求。然后我们需要用到另一个 trick:将 $p$ 向 $p_0$ 连一条长度为 $(p-p_0)\times w_{p_0}$ 的边,这样连出来一定是棵树。(我也不太清楚这个 trick 的名字,可能跟 AC 自动机的优化差不多。)

这样我们可以在树上二分出最后一个不大于 $r-k+1$ 的 $p_0$。具体实现:先一遍 dfs 将 $dep_i$ 和 $dis_i$ 求出,其中 $dp_i$ 表示 $i$ 的深度,$dis_i$ 表示 $i$ 到根节点(为了方便设为 $n-m+2$)的距离。接着将二分的左起始点设为 $1$,右起始点设为 $dep_l$,判断 $l$ 在树上的 $dep_l-mid$ 级祖先是否小于等于 $r-k+1$,移动 $l$ 和 $r$ 进行二分,设二分出来的答案为 $res$。这时答案就为 $dis_l-dis_{res}+(r-k-res+2)\times w_{res}$。

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;

int T,n,m,q,a[200005],w[200005];

vector <int> to[200005];

int st[200005],top;

namespace SGT{
	int t[400005<<2];
	inline void pushup(int k){
		t[k]=max(t[k<<1],t[k<<1|1]);return;	
	}
	inline void build(int k=1,int l=1,int r=2*n){
		if(l==r){
			t[k]=0;
		}else{
			int mid=l+r>>1;
			build(k<<1,l,mid),build(k<<1|1,mid+1,r);
			pushup(k);
		}return;
	}
	inline void updata(int P,int val,int k=1,int l=1,int r=2*n){
		if(P<=l&&r<=P){
			t[k]+=val;
		}else{
			int mid=l+r>>1;
			if(P<=mid)updata(P,val,k<<1,l,mid);
			else updata(P,val,k<<1|1,mid+1,r);
			pushup(k);
		}return;
	}
}

namespace SP{
	#define Rana_Love true
	int siz[200005],dfn[200005],dep[200005],fa[200005],top[200005],mxs[200005],fp[200005],dis[200005],cnt;
	inline void dfs1(int x,int last){
		dep[x]=dep[last]+1,fa[x]=last,siz[x]=1;
		for(int y:to[x]){
			if(y==last)continue;
			dis[y]=dis[x]+(x-y)*w[y];
			dfs1(y,x),siz[x]+=siz[y];
			if(siz[y]>siz[mxs[x]])mxs[x]=y;
		}return;
	}
	inline void dfs2(int x,int last){
		dfn[x]=++cnt,fp[cnt]=x,top[x]=last;
		if(!mxs[x])return;dfs2(mxs[x],last);
		for(int y:to[x]){
			if(y==fa[x]||y==mxs[x])continue;
			dfs2(y,y);
		}return;
	}
	inline void init(){
		cnt=0;for(int i=1;i<=n-m+2;i++)mxs[i]=0;dfs1(n-m+2,0),dfs2(n-m+2,n-m+2);return;
	}
	inline int jump(int x,int deep){
		int step=dep[x]-deep;
		while(Rana_Love){	
			if(dep[x]-dep[top[x]]+1<=step){
				step+=dep[top[x]]-dep[x]-1,x=fa[top[x]];
			}else break;
		}return fp[dfn[x]-step];
	}
	inline int solve(int st,int ed){
		int l=1,r=dep[st];
		while(l<r){
			int mid=l+r>>1;
			if(jump(st,mid)<=ed-m+1){
				r=mid;
			}else l=mid+1;
		}r=jump(st,r);
		return dis[st]-dis[r]+w[r]*(ed-m+1-r+1);
	}
}

inline int read(){
	register int x(0),t(0);
	static char ch=getchar();
	while(!isdigit(ch)){t|=(ch=='-'),ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48),ch=getchar();}
	return t?-x:x;
}

signed main(){
	T=read();while(T--){
	
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++){
		a[i]=read()+n-i;
	}
	for(int i=1;i<=n-m+2;i++){
		to[i].clear();
	}
	SGT::build();
	for(int i=1;i<=m;i++){
		SGT::updata(a[i],1);
	}w[1]=m-SGT::t[1];
	for(int i=2;i+m-1<=n;i++){
		SGT::updata(a[i-1],-1);
		SGT::updata(a[i+m-1],1);
		w[i]=m-SGT::t[1];
	}
	st[top=0]=n-m+2;
	for(int i=n-m+1;i>=1;i--){
		while(top&&w[st[top]]>=w[i])top--;
		to[st[top]].push_back(i),st[++top]=i;
	}
	SP::init();
	while(q--){
		int l=read(),r=read();
		cout<<SP::solve(l,r)<<'\n';
	}
	
    }return 0;
}

我的树上 k 级祖先是用重链剖分求的,因此时间复杂度为 $O(n\times \log^2 n)$。可以将重链剖分换成长链剖分之类的,时间复杂度可以降到 $O(n\times\log n)$。或许可以直接上历史和线段树?

G3

G3要求的东西变为了 $\sum \limits_{i=l}^{r-k+1} \sum \limits_{j=i}^{r-k+1} \min \limits_{g=i}^{j} w_j$。类似这种式子套上 P3246 [HNOI2016] 序列 的方法就行了。不知道能否将G2的方法继续扩展到G3,我还没想出来,如果可以的话还请告诉我!

这里仅仅贴一份用莫队实现的代码,具体思路参考 P3246 [HNOI2016] 序列 的题解:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int lim=200000,block=447;

int lg[200005];

int T,n,m,len,a[200005],w[200005],ans[200005];

namespace SGT{
	int t[400005<<2];
	inline void pushup(int k){
		t[k]=max(t[k<<1],t[k<<1|1]);return;	
	}
	inline void build(int k=1,int l=1,int r=2*n){
		if(l==r){
			t[k]=0;
		}else{
			int mid=l+r>>1;
			build(k<<1,l,mid),build(k<<1|1,mid+1,r);
			pushup(k);
		}return;
	}
	inline void updata(int P,int val,int k=1,int l=1,int r=2*n){
		if(P<=l&&r<=P){
			t[k]+=val;
		}else{
			int mid=l+r>>1;
			if(P<=mid)updata(P,val,k<<1,l,mid);
			else updata(P,val,k<<1|1,mid+1,r);
			pushup(k);
		}return;
	}
}

int dp[2][200005];

namespace ST{
	int f[200005][25],pre[200005],nxt[200005],st[200005],top;
	inline int comp(int A,int B){
		return w[A]<w[B]?A:B;
	}
	inline void init(){
		for(int i=1;i<=n-len+1;i++){
			f[i][0]=i;
		}
		for(int j=1;1<<j<=n-len+1;j++){
			for(int i=1;i+(1<<j)-1<=n-len+1;i++){
				f[i][j]=comp(f[i][j-1],f[i+(1<<j-1)][j-1]);
			}
		}
		dp[0][st[top=0]=0]=0;
		for(int i=1;i<=n-len+1;i++){
			while(top&&w[st[top]]>=w[i])top--;
			pre[i]=st[top],st[++top]=i;
		}
		for(int i=1;i<=n;i++){
			dp[0][i]=dp[0][pre[i]]+(i-pre[i])*w[i];
		}
		dp[1][st[top=0]=n-len+2]=0;
		for(int i=n-len+1;i>=1;i--){
			while(top&&w[st[top]]>=w[i])top--;
			nxt[i]=st[top],st[++top]=i;
		}
		for(int i=n-len+1;i>=1;i--){
			dp[1][i]=dp[1][nxt[i]]+(nxt[i]-i)*w[i];
		}
		return;
	}
	inline int query(int l,int r){
		return comp(f[l][lg[r-l+1]],f[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
	}
}

int lt,rt,res;

struct Rana{
	int l,r,id;
}q[200005];

inline int idx(int x){return (x-1)/block+1;}

inline bool cmp(Rana A,Rana B){
	return idx(A.l)!=idx(B.l)?idx(A.l)<idx(B.l):idx(A.r)<idx(B.r);
}

inline int left(int l,int r){
	int pos=ST::query(l,r);
	return w[pos]*(r-pos+1)+dp[1][l]-dp[1][pos];
}

inline int right(int l,int r){
	int pos=ST::query(l,r);
	return w[pos]*(pos-l+1)+dp[0][r]-dp[0][pos];
}

inline int read(){
	register int x(0),t(0);
	static char ch=getchar();
	while(!isdigit(ch)){t|=(ch=='-'),ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48),ch=getchar();}
	return t?-x:x;
}

signed main(){
	for(int i=2;i<=lim;i++){
		lg[i]=lg[i>>1]+1;
	}T=read();while(T--){
	
	n=read(),len=read(),m=read();
	for(int i=1;i<=n;i++){
		a[i]=read()-i+n;
	}
	SGT::build();
	for(int i=1;i<=len;i++){
		SGT::updata(a[i],1);
	}w[1]=len-SGT::t[1];
	for(int i=2;i+len-1<=n;i++){
		SGT::updata(a[i-1],-1);
		SGT::updata(a[i+len-1],1);
		w[i]=len-SGT::t[1];
	}
	ST::init();
	for(int i=1;i<=m;i++){
		q[i]={read(),read()-len+1,i};
	}
	sort(q+1,q+1+m,cmp),lt=1,rt=0,res=0;
	for(int i=1;i<=m;i++){
		while(lt>q[i].l)res+=left(--lt,rt);
		while(rt<q[i].r)res+=right(lt,++rt);
		while(lt<q[i].l)res-=left(lt++,rt);
		while(rt>q[i].r)res-=right(lt,rt--);
		ans[q[i].id]=res;
	}
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<'\n';
	}
	
	}return 0;
}

标签:res,200005,int,题解,top,st,--,Queries,Yunli
From: https://www.cnblogs.com/PNNNN/p/18401619

相关文章

  • RecyclerView 高效使用与常见问题解决
    RecyclerView是Android应用开发中最常用的UI组件之一,通常用于显示大量数据列表。尽管功能强大,但如果使用不当,会导致性能问题、数据错乱或滚动卡顿等问题。在本篇文章中,我们将探讨RecyclerView的一些常见坑点,提供解决方案,并附带代码示例。1.坑点:ViewHolder重用导致数据错乱......
  • ctfshow web红包题第二弹题解
    从今天开始记录刷题日常打开靶场平平无奇,看源代码发现如下提示get方式提交cmd参数,猜测是命令执行漏洞,先写个phpinfo();试试。有用,但报错cerror查看显示出来部分php代码,过滤了很多东西if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)) 第一个正则表达式把字母数字几乎全......
  • [ABC137F] Polynomial Construction 题解
    明明有最厉害最好想的插值做法,怎么没有人写呢。思路考虑\(n\)个点可以确定一个\(n-1\)次多项式。如何确定。令\(l_i(x)=\prod_{j\not=i}\frac{(x-x_j)}{(x_i-x_j)}\)。可以发现这个多项式在\(x=x_i\)时值为一,在\(x=x_j(j\not=i)\)时值为零。那么就有:\[F(x)=\su......
  • 洛谷 P3034 Cow Photography G/S——题解
    洛谷P3034题解传送锚点摸鱼环节[USACO11DEC]CowPhotographyG/S题面翻译题目描述今天的奶牛们特别调皮!FarmerJohn想做的只是给排成一排的奶牛拍照,但是在他拍下照片之前,奶牛们一直在移动。具体地说,FJ有\(N\)头奶牛(\(1\leqN\leq20\,000\)),每头奶牛都有一个唯一确......
  • 题解:AT_abc369_e [ABC369E] Sightseeing Tour 详细版
    题目大意给定一个NNN个点,MMM条边的无向图。其中边有边权。有......
  • MySQL 日期函数语法介绍和案例示范以及常见问题解决
    本文将以电商交易系统为例,详细讲解MySQL日期类型及其转化,常用的日期函数,以及一些解决常见问题的方案。一、MySQL日期数据类型MySQL提供了多种日期数据类型,适用于不同的使用场景。常见的日期类型包括DATE、DATETIME、TIMESTAMP、TIME和YEAR。DATE:只存储日期,不包含......
  • 题解:SP3693 KGSS - Maximum Sum
    原题传送门思路分析线段树。这道题让我们进行两种操作,分别是单点修改和区间查询,结合数据范围,很明显是一道线段树。区间里最大的\(A_i+A_j\),其实就是求区间里的最大值和次大值,我们用线段树维护最大值和次大值。建树voidbuild(intnow,inttl,inttr){ if(tl==tr){ tmax......
  • P6638 「JYLOI Round 1」常规 题解
    题目传送门前置知识可持久化线段树|前缀和&差分解法进行差分,区间查询转化成前缀和相减。先将\(\{a\}\)升序排序。设当前询问的区间为\([1,r]\),在\(\{a\}\)中找到一个最大的\(pos\)使得\(a_{pos}\ler\),则\([1,r]\)中所做常规的次数为\(\sum\limits_{i=1......
  • 使用flask进行Mock Server模拟接口操作及问题解决
    1.flask介绍flask是一个轻量级的pythonweb微框架2.MockServer介绍MockServer是一个开源的模拟服务器,它可以定义和记录API交互,支持各种http方法(get、post、put、delete),可以自定义响应内容,例如返回静态文件可以使用flask来搭建一个mock模拟服务3.模拟接口先安装flaskpip......
  • P1928 外星密码题解
    初看这题时,感觉就是一个简简单单的递归,便有了以下代码:#include <bits/stdc++.h>using namespace std;string re(){    string s="",s1="";    char c;    int n;    while(cin>>c){        if(c=='['){            cin>>n;......