首页 > 其他分享 >【题解】[Ynoi2013] 文化课

【题解】[Ynoi2013] 文化课

时间:2023-02-15 18:03:17浏览次数:36  
标签:lmul rlen rmul int 题解 Ynoi2013 文化课 res now

题目分析:

这个权值一看就可以使用线段树维护啊,因为很明显可以进行高效合并。
对于区间合并其实就只是需要判断一下两个区间中间如果是乘号,那么造成的贡献要变成区间两边乘起来的贡献,而不是区间两边加起来的贡献。
所以我们其实就是要维护这么几个信息:\(lmul,rmul\) 区间左边/右边极长的一段乘的结果,\(opt\) 区间最右边的符号是什么,\(res\) 这个区间的权值是多少,\(sz\) 代表这个区间的长度。

这样我们就顺利地完成了不带修改的问题,那么考虑带上修改怎么做呢。
如果是修改符号的话显然就可以直接修改这几个信息,只是需要多维护 \(sum\) 代表区间和,\(mul\) 代表区间乘,用来替换 \(res\),\(lx\) 代表区间最左边的数是多少,因为无论怎么修改 \(lmul\) 至少也要是 \(lx\)。
而如果是修改数值的话其实不是很容易,假设当前区间内,长度为 \(i\) 的连续乘的极长的段的数量为 \(cnt[i]\),那么意味着我们的权值会变成 \(\sum_{i=1} x^i cnt[i]\)。
如果我们直接对于 \(cnt\) 把数组开到 \(n\) 的话空间复杂度就是 \(O(n^2)\) 就很显然不可以。
这个时候就有一个想法就是分块,假设我们的区间长度为 \(len\),那么我们的 \(cnt\) 数组只开到 \(\sqrt{len}\),对于大于 \(\sqrt{len}\) 的部分,我们不以桶的形式存储而是直接以数值存到数组里,因为大于 \(\sqrt{len}\) 的部分的个数不会超过 \(\sqrt{len}\) 个,所以总的空间复杂度就是 \(O(n\sqrt{n})\)。处理了这个那么我们的修改数值就很简单了。

此时我们在区间合并的时候也要记得把 \(cnt\) 数组和记录的数组进行合并。

这样的话单次操作复杂度就是 \(O(\sqrt{n}) + O(\sqrt{\frac{n}{2}}) + \cdots = O(\sqrt{n})\)

这个题即卡时间又卡空间,所以需要实现的精细一点。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100500;
const int MOD = 1e9+7;
long long a[N],b[N];
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*10+ch-48;ch=getchar();}
	return x*f;
}
int mod(int x){
	if(x < 0)	return (x % MOD + MOD) % MOD;
	return x % MOD;
}
int power(int a,int b){
	int res = 1;
	while(b){
		if(b & 1)	res = mod(res * a);
		a = mod(a * a);
		b >>= 1;
	}
	return res;
}
struct SGT{
	int sum,mul,lx,tagx,tago,mx,sz,llen,lmul,rlen,rmul,res;
	bool opt;
	int *cnt;
	vector<int> v;
	void init(int a,bool b){
		res = lx = mul = sum = a;
		lmul = a,llen = 1;opt = b;
		if(b)	rmul = a,rlen = 1;
		else	rmul = 1,rlen = 0;
		cnt[0] = 0;cnt[1] = 1;
	}
	void init(int len){
		mx = sqrt(len);sz = len;cnt = new int[mx+1];
		for(int i=0; i<=mx; i++)	cnt[i] = 0;
		tagx = tago = -1;mul = 1;
	}
	void updatex(int x){
		res = 0;sum = mod(x * sz);mul = power(x,sz);
		lx = x,lmul = power(x,llen),rmul = power(x,rlen);
		for(int i=1,tmp=x; i<=mx; i++,tmp=mod(tmp*x))	res = mod(res + cnt[i] * tmp); 
		//emmm 乘法忘取模问题严重 
		for(int i : v)	res = mod(res + power(x,i));
		tagx = x;
	}
	void updateo(bool o){
		opt = tago = o;
		for(int i=0; i<=mx; i++)	cnt[i] = 0;
		v.clear();
		if(o == 1){
			if(sz > mx)	v.push_back(sz);
			else	++cnt[sz];
			res = mul;llen = rlen = sz;
			lmul = rmul = mul;
		}
		else{
			res = sum;cnt[1] = sz;
			llen = 1,lmul = lx;   //默认数和右边的运算符绑定 
			rlen = 0,rmul = 1;
		}
	}
	void pushdown(SGT &l,SGT &r){   //草,忘记了这里要引用 
		if(tagx != -1)	l.updatex(tagx),r.updatex(tagx),tagx = -1;
		if(tago != -1)	l.updateo(tago),r.updateo(tago),tago = -1;
	}
	void pushup(SGT &l,SGT &r,bool flag){
		opt = r.opt;sum = mod(l.sum + r.sum);mul = mod(l.mul * r.mul);lx = l.lx;
		if(l.llen == l.sz && l.opt == 1)	llen = l.llen + r.llen,lmul = mod(l.lmul * r.lmul);
		else	llen = l.llen,lmul = l.lmul;
		if(r.rlen == r.sz && l.opt == 1)	rlen = l.rlen + r.rlen,rmul = mod(l.rmul * r.rmul);
		else	rlen = r.rlen,rmul = r.rmul;
		if(l.opt == 1)	res = mod(l.res + r.res - l.rmul - r.lmul + l.rmul * r.lmul);
		else	res = mod(l.res + r.res);
		
		if(!flag)	return;
		for(int i=0; i<=mx; i++)	cnt[i] = 0;
		v.clear();
		int tmp1 = 0,tmp2 = 0,h = 0;
		if(l.opt == 1)	tmp1 = l.rlen,tmp2 = r.llen,h = tmp1 + tmp2;
		if(tmp1 && tmp1 <= mx)	cnt[tmp1]--,tmp1 = 0;
		if(tmp2 && tmp2 <= mx)	cnt[tmp2]--,tmp2 = 0;
		if(h && h <= mx)	cnt[h]++;
		else if(h)	v.push_back(h);
		for(int i=1; i<=l.mx; i++)	cnt[i] += l.cnt[i];  //一定要注意不要在这里指针越界啊!!! 
		for(int i=1; i<=r.mx; i++)	cnt[i] += r.cnt[i];
		for(int i : l.v){
			if(i == tmp1)	tmp1 = 0;
			else if(i == tmp2)	tmp2 = 0;
			else if(i <= mx)	cnt[i]++;
			else	v.push_back(i);
		}
		for(int i : r.v){
			if(i == tmp1)	tmp1 = 0;
			else if(i == tmp2)	tmp2 = 0;
			else if(i <= mx)	cnt[i]++;
			else	v.push_back(i);
		}
	}
}t[4*N];
struct node{
	int llen,lmul,rlen,rmul,res,opt,sz;
	//opt 维护区间最右侧的那个符号 
};
node merge(node a,node b){
	node c;
	c.sz = a.sz + b.sz;c.opt = b.opt;
	if(a.llen == a.sz && a.opt == 1)	c.llen = a.llen + b.llen,c.lmul = mod(a.lmul * b.lmul);
	else	c.llen = a.llen,c.lmul = a.lmul;
	if(b.rlen == b.sz && a.opt == 1)	c.rlen = a.rlen + b.rlen,c.rmul = mod(a.rmul * b.rmul);
	else	c.rlen = b.rlen,c.rmul = b.rmul;
	if(a.opt == 1)	c.res = mod(a.res + b.res - a.rmul - b.lmul + a.rmul * b.lmul);
	else	c.res = mod(a.res + b.res);
	return c;
}
void build(int now,int now_l,int now_r){
	t[now].init(now_r - now_l + 1);
	if(now_l == now_r){
		t[now].init(a[now_l],b[now_l]);
		return;
	}
	int mid = (now_l + now_r)>>1;
	build(now<<1,now_l,mid);build(now<<1|1,mid+1,now_r);
	t[now].pushup(t[now<<1],t[now<<1|1],true);
}
void modifyx(int now,int now_l,int now_r,int l,int r,int x){
	if(l <= now_l && r >= now_r){
		t[now].updatex(x);
//		printf("t[%lld]=%lld,%lld,%lld\n",now,t[now].opt,t[now].lx,t[now].res);
		return;
	}
	t[now].pushdown(t[now<<1],t[now<<1|1]);
	int mid = (now_l + now_r)>>1;
	if(l <= mid)	modifyx(now<<1,now_l,mid,l,r,x);
	if(r > mid)		modifyx(now<<1|1,mid+1,now_r,l,r,x);
	t[now].pushup(t[now<<1],t[now<<1|1],false);
//	printf("t[%lld]=%lld,%lld,%lld\n",now,t[now].opt,t[now].lx,t[now].res);
}
void modifyo(int now,int now_l,int now_r,int l,int r,bool x){
	if(l <= now_l && r >= now_r){
		t[now].updateo(x);
		return;
	}
	t[now].pushdown(t[now<<1],t[now<<1|1]);
	int mid = (now_l + now_r)>>1;
	if(l <= mid)	modifyo(now<<1,now_l,mid,l,r,x);
	if(r > mid)		modifyo(now<<1|1,mid+1,now_r,l,r,x);
	t[now].pushup(t[now<<1],t[now<<1|1],true);
}
node query(int now,int now_l,int now_r,int l,int r){
//	printf("t[%lld]=%lld,%lld,%lld\n",now,t[now].llen,t[now].rlen,t[now].res);
	if(l <= now_l && r >= now_r)	return {t[now].llen,t[now].lmul,t[now].rlen,t[now].rmul,t[now].res,t[now].opt,t[now].sz};
	t[now].pushdown(t[now<<1],t[now<<1|1]);
	int mid = (now_l + now_r)>>1;
	if(r <= mid)	return query(now<<1,now_l,mid,l,r);
	if(l > mid)		return query(now<<1|1,mid+1,now_r,l,r);
	return merge(query(now<<1,now_l,mid,l,r),query(now<<1|1,mid+1,now_r,l,r));
}
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("ans.txt","w",stdout);
	int n,m;n=read(),m=read();
	for(int i=1; i<=n; i++)		a[i] = read(),a[i] = mod(a[i]);
	for(int i=1; i<=n-1; i++)	b[i] = read();
	build(1,1,n);
	for(int i=1; i<=m; i++){
		int opt,l,r,x;opt = read();l=read();r=read();
		if(opt != 3)	x=read();
		if(opt == 1)	modifyx(1,1,n,l,r,x%MOD);
		else if(opt == 2)	modifyo(1,1,n,l,r,x);
		else if(opt == 3)	printf("%lld\n",query(1,1,n,l,r).res);
	}
	return 0;
}

标签:lmul,rlen,rmul,int,题解,Ynoi2013,文化课,res,now
From: https://www.cnblogs.com/linyihdfj/p/17123897.html

相关文章

  • 【题解】CF280D k-Maximum Subsequence Sum
    题目分析:(可能是刚做完毒瘤Ynoi的原因,看这个4k的线段树感觉好简单)可以看一下这个查询的操作,最多\(k\)个不重线段的和的最大值,这个东西大概是网络流的经典题吧。具......
  • 【题解】Luogu P3939 数颜色
    题目分析:解法一:显然我们可以直接对每一种颜色建一棵线段树,然后剩下的操作就非常简单了。代码:点击查看代码#include<bits/stdc++.h>usingnamespacestd;constint......
  • hadoop之shuffle阶段相关面试题解析
    --思考1:map()方法写出的数据存储到哪里?                                  --内存中1、在内存中存有一个环形缓冲区,该缓冲......
  • [省选联考 2022] 填树 题解
    神奇dp。发现我看到dp大多数时候只会暴力。那我约等于退役了啊。题意:自己看。首先有一个显然的暴力。枚举一个最小值\(L\),然后区间就限定在了\([L,L+K]\)。那么普及......
  • NOIP2015 普及组 推销员题解
    原题链接给定一个数轴,数轴上有一些点,第\(i\)个点离起点的距离是\(S_i\),取走它要消耗\(A_i\)的代价,同时在数轴上每移动一格要\(1\)的代价,路线只能从数轴......
  • CSP2022 假期计划 题解
    给你一张\(n\)个点,\(m\)条边的无向图,每个点有点权,要求你在图中选出\(A\),\(B\),\(C\),\(D\)四个点,使得\(1\rightarrowA\rightarrowB\rightarrowC\righ......
  • DTOJ 2023.02.11 测试 题解
    DTOJ2023.02.11测试题解2023省选模拟Round#12\(100+8+50=158\)还行T2想到了,但是没写,我觉得写了也不一定写得出来,挺妙的T1题意http://59.61.75.5:18018/p/545......
  • 读者最需要什么样的题解
    哈哈,其实还是鲜花,主要是看到\(\text{f}\color{red}{\text{eecle6418}}\)的这篇题解有感而发,当然我自己写的题解也很抽象,需要改正。当然这里的写题解是指主动打算写一......
  • 小孩召开法 题解
    开题之前の一些废话:小孩召开法,旦写一北砬。梦厷停留在,破了大式様。——龚诗锋《炄勺,砒》小孩。又是我很不会的排列计数。而且神题。久仰大名。现在是2月13日......
  • ARC120C Swaps 2 题解
    好难啊,会也不会设\(a_i=x,a_{i+1}=y\),那么交换后\(a_i=y+1,a_{i+1}=x-1\),发现交换后就是\(a_i+i\)和\(a_{i+1}+i+1\)这两个值进行了交换。那就把所有\(a_i\)变成......