首页 > 其他分享 >【XSY4041】搬砖(线段树)

【XSY4041】搬砖(线段树)

时间:2022-10-31 07:35:14浏览次数:65  
标签:ch gcd 线段 XSY4041 kl cdots aligned equiv

题面

搬砖

题解

题意为求最大的 \(p\) 使得 \(h_1\equiv h_2\equiv \cdots\equiv h_n\pmod p\)。

即 \(h_2-h_1\equiv h_3-h_2\equiv \cdots\equiv h_n-h_{n-1}\equiv 0\pmod p\)。

那么我们可以得到 \(p|\gcd(h_2-h_1,h_3-h_2,\cdots,h_n-h_{n-1})\)。

令 \(c_i=h_{i+1}-h_i\),那么最大的 \(p\) 就是 \(\gcd(c_1,c_2,\cdots,c_{n-1})\)。

那么我们要做的就是动态维护差分下的全局 \(\gcd\)。

考虑修改操作,对于操作一 \(L,R,a,b,c\) 来说:(其中 \(x\in [L,R)\),注意 \(c_x\) 和 \(c\) 的区分)

\[\begin{aligned} c_x&=h_{x+1}-h_x=\left[a(x+1)^2+b(x+1)+c\right]-\left[ax^2+bx+c\right]\\ &=a(2x+1)+b=2ax+a+b \end{aligned} \]

令 \(k=2a\),\(m=a+b\),由 \(\gcd(a,b)=\gcd(a,b-a)\) 可知对于任意的 \(L\leq l<r< R\),有:

\[\begin{aligned} \gcd(c_{l},c_{l+1},\cdots,c_{r})&=\gcd(kl+m,k(l+1)+m,\cdots,k(r-2)+m,k(r-1)+m,kr+m)\\ &=\gcd(kl+m,k(l+1)+m,\cdots,k(r-2)+m,k(r-1)+m,k)\\ &=\gcd(kl+m,k,k,\cdots,k)\\ &=\gcd(kl+m,k)\\ &=\gcd(m,k)\\ \end{aligned} \]

那么我们用两棵线段树,一棵 \(T_1\) 维护 \(h_i\),另一棵 \(T_2\) 维护 \(\gcd(c_i)\),然后操作一就相当于在 \(T_1\) 上区间二次函数重置、在 \(T_2\) 上区间重置和单点修改。

而操作二就是来蒙人的……注意到 \(a=b=0\),除了端点外 \(c_i\) 不变,那么我们只需要在 \(T_1\) 上区间加、在 \(T_2\) 上单点修改。

时间复杂度 \(O(n\log n\log w)\),其中 \(n,q\) 同阶,\(w\) 为值域最大值。

#include<bits/stdc++.h>

#define N 200010
#define ll __int128

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

inline void write(ll x,char c)
{
	static int a[55];
	int cnt=0;
	while(x)
	{
		a[++cnt]=x%10;
		x/=10;
	}
	for(int i=cnt;i>=1;i--)
		putchar(a[i]+'0');
	putchar(c);
}

ll Abs(ll x)
{
	return x<0?-x:x;
}

ll gcd(ll a,ll b)
{
	if(!a||!b) return a+b;
	return b?gcd(b,a%b):a;
}

struct data
{
	int a,b,c;
	data(){};
	data(int aa,int bb,int cc){a=aa,b=bb,c=cc;}
};

int n,q,h[N];

namespace T1
{
	data lazy[N<<2];
	bool tag[N<<2];
	ll lazadd[N<<2];
	void downn(int k,data v)
	{
		lazy[k]=v;
		lazadd[k]=0;
		tag[k]=1;
	}
	void down(int k)
	{
		if(tag[k])
		{
			downn(k<<1,lazy[k]);
			downn(k<<1|1,lazy[k]);
			tag[k]=0;
		}
		if(lazadd[k])
		{
			lazadd[k<<1]+=lazadd[k];
			lazadd[k<<1|1]+=lazadd[k];
			lazadd[k]=0;
		}
	}
	void build(int k,int l,int r)
	{
		if(l==r)
		{
			h[l]=lazadd[k]=read();
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
	}
	void update(int k,int l,int r,int ql,int qr,data v)
	{
		if(ql<=l&&r<=qr)
		{
			downn(k,v);
			return;
		}
		down(k);
		int mid=(l+r)>>1;
		if(ql<=mid) update(k<<1,l,mid,ql,qr,v);
		if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,v);
	}
	void change(int k,int l,int r,int ql,int qr,int v)
	{
		if(ql<=l&&r<=qr)
		{
			lazadd[k]+=v;
			return;
		}
		down(k);
		int mid=(l+r)>>1;
		if(ql<=mid) change(k<<1,l,mid,ql,qr,v);
		if(qr>mid) change(k<<1|1,mid+1,r,ql,qr,v);
	}
	ll query(int k,int l,int r,int x)
	{
		if(l==r)
		{
			ll ans=0;
			if(tag[k]) ans=(ll)lazy[k].a*l*l+(ll)lazy[k].b*l+lazy[k].c;
			ans+=lazadd[k];
			return ans;
		}
		down(k);
		int mid=(l+r)>>1;
		if(x<=mid) return query(k<<1,l,mid,x);
		return query(k<<1|1,mid+1,r,x);
	}
}

namespace T2
{
	int leaf[N<<2];
	ll g[N<<2],lazy[N<<2];
	bool tag[N<<2];
	void up(int k)
	{
		g[k]=gcd(g[k<<1],g[k<<1|1]);
	}
	void build(int k,int l,int r)
	{
		if(l==r)
		{
			leaf[k]=l;
			g[l]=Abs(h[l+1]-h[l]);
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		up(k);
	}
	void downn(int k,ll v)
	{
		if(!leaf[k])
		{
			g[k]=lazy[k]=v;
			tag[k]=1;
		}
	}
	void getval(int k)
	{
		g[k]=Abs(T1::query(1,1,n,leaf[k]+1)-T1::query(1,1,n,leaf[k]));
	}
	void down(int k)
	{
		if(tag[k])
		{
			downn(k<<1,lazy[k]);
			downn(k<<1|1,lazy[k]);
			tag[k]=0;
		}
		if(leaf[k<<1]) getval(k<<1);
		if(leaf[k<<1|1]) getval(k<<1|1);
	}
	void update(int k,int l,int r,int ql,int qr,ll v)
	{
		if(ql<=l&&r<=qr)
		{
			downn(k,v);
			return;
		}
		down(k);
		int mid=(l+r)>>1;
		if(ql<=mid) update(k<<1,l,mid,ql,qr,v);
		if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,v);
		up(k);
	}
	void change(int k,int l,int r,int x)
	{
		if(l==r) return;
		down(k);
		int mid=(l+r)>>1;
		if(x<=mid) change(k<<1,l,mid,x);
		else change(k<<1|1,mid+1,r,x);
		up(k);
	}
}

int main()
{
	n=read(),q=read();
	T1::build(1,1,n);
	T2::build(1,1,n-1);
	while(q--)
	{
		int opt=read(),l=read(),r=read(),a=read(),b=read(),c=read();
		if(opt==1)
		{
			T1::update(1,1,n,l,r,data(a,b,c));
			if(1<=l-1) T2::change(1,1,n-1,l-1);
			if(r<=n-1) T2::change(1,1,n-1,r);
			if(l==r-1) T2::change(1,1,n-1,r-1);
			else if(l<r-1) T2::update(1,1,n-1,l,r-1,gcd(a+b,2*a));
		}
		if(opt==2)
		{
			T1::change(1,1,n,l,r,c);
			if(1<=l-1) T2::change(1,1,n-1,l-1);
			if(r<=n-1) T2::change(1,1,n-1,r);
		}
		int ans=T2::g[1];
		if(!ans) assert(0),ans=T1::query(1,1,n,1);
		write(ans,'\n');
	}
	return 0;
}
/*
5 3
2 2 4 8 12
1 1 3 0 0 4
2 3 5 0 0 8
1 1 5 4 2 4
*/
//不知道为什么95pts过不去……

标签:ch,gcd,线段,XSY4041,kl,cdots,aligned,equiv
From: https://www.cnblogs.com/ez-lcw/p/16842961.html

相关文章

  • P1803 凌乱的yyy / 线段覆盖
    题目背景快noip了,yyy很紧张!题目描述现在各大oj上有 nn 个比赛,每个比赛的开始、结束的时间点是知道的。yyy认为,参加越多的比赛,noip就能考的越好(假的)。所以......
  • 【XSY3810】公路建设(线段树,kruskal)
    题面题解一开始竟然没反应过来是最小生成树。考虑用线段树直接维护每个区间的答案。注意到一个区间最多只有\(n-1\)条树边有用,所以线段树每个节点用一个vector按权......
  • 【XSY3551】Inserting Lines(线段树)
    题意:数轴上有无穷个格子,每个坐标上各有一个格子,你需要支持以下操作共\(n\)次:在\(x=k\)这个格子前插入一个格子,并将所有\(x\geqk\)的格子后移一位。时间++。询问......
  • 【XSY3549】Tree(线段树,换根)
    原题不想说(太懒了),就说一下总结到的两点想法?对于树上多次询问路径信息的问题,如果两条路径的信息无法快速合并(即不能pushup),但是在路径两端增加/删除单点后的信息变化可以......
  • 基本线段树
    线段树P3372【模板】线段树1已知一个数列ai,有下列两种操作将区间[x,y]内每个数加上k求区间[x,y]中每个数的和线段树的思想便是将数列a,用若干个区间,在树上用结点表......
  • 【XSY3490】线段树(广义线段树,树上莫队)
    题面线段树题解本题分两Part走。Part1我们需要解决:如何在广义线段树上快速区间定位节点。对于有\(n\)个叶子节点、共\(2n-1\)个节点的广义线段树\(A\),我们......
  • 【XSY3434】【UOJ431】time map(线段树维护位运算)
    首先注意到一个性质:如果我们把权值相同且相邻的点归为一个连通块的话,那么一个叶子节点往上会经过至多\(O(\logV)\)个连通块(因为父亲节点一定是儿子节点的子集)。又注意......
  • 【XSY3326】米缸(时间复杂度均衡,线段树,基环树,倍增)
    时间复杂度的均衡。先考虑暴力的想法:显然这是一棵基环树,那么我们每次修改时暴力\(O(nm)\)重构基环树,然后询问的时候就能\(O(1)\)查询。时间复杂度\(O(nmq)\)。考虑......
  • 【XSY3367】青春野狼不做姐控偶像的梦(线段树)
    题意:给一个\(1\simn\)的排列,多次询问某段区间内的值域连续子区间的个数。区间值域连续的另一种表达方式:\(max-min=r-l\),即\((max-min)-(r-l)=0\)。考虑\(l=1,r=n\)......
  • CG2017 PA1-2 Crossroad (十字路口) 暴力求解2 线段、射线、直线、圆两两相交的简单
    题目是上一个随笔的题目,这次只判断交点个数不求出具体坐标,还是72.5分,看来卡O(n^2)复杂度卡得死死的。这次的代码给出了简单的线段、射线、直线、圆两两相交的判断交点个数......