Java教程

队内训练5 线段树魔改

本文主要是介绍队内训练5 线段树魔改,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

队内训练5 线段树魔改

题目链接link.

真就一眼思路然后调了一天。

题意:一个数组,两种操作,第一种区间加v,第二种求区间内 ∑ i = l r ∑ j = i + 1 r a i ∗ a j \sum_{i=l}^r\sum_{j=i+1}^r{a_i*a_j} ∑i=lr​∑j=i+1r​ai​∗aj​.

这个题,就,还行,线段树进阶吧属于是,但是思路还挺好出的。首先考虑维护两个值,一个sum一个value,两个值一起更新。当两个线段合并的时候,我们可以轻易推出:

v a l u e = v a l u e 1 + v a l u e 2 + s u m 1 ∗ s u m 2 value=value1+value2+sum1*sum2 value=value1+value2+sum1∗sum2.

当然啦这只是合并的时候的式子,我们还需要考虑懒标记下放更新的时候怎么更新。

易知,当一个区间同时加上w时,更新的方式应该是:

v a l u e + = ( w 2 ∗ l e n ∗ ( l e n − 1 ) / 2 + w ∗ ( l e n − 1 ) ∗ s u m ) value+=(w^2*len*(len-1)/2+w*(len-1)*sum) value+=(w2∗len∗(len−1)/2+w∗(len−1)∗sum).

然后改一改线段树就好啦。

说一下wa一天的原因吧。

第一个wa的点是,我的 lazy_tag 初始化的值是-1,后面update的时候,直接+=w,显然很傻逼的错误,不知道怎么的误打误撞过了样例,可能对线段树过于自信也没有再手造数据疯狂改取模和全局int128,最后还是池池找出来错哪了。呜呜。

第二个wa的点,就是,可以说是奇闻共赏了:
在这里插入图片描述
笑拉了家人们。看看我的return 0,最离谱的是,我对拍的时候,为了方便自己找错误,直接设置了t=1,对拍跑了一年跑不出差异。笑死。

AC代码:

#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int mod=1e9+7;
int n;

struct qaq
{
	int l,r;
	int value=0;
	int sum=0;
	int lazy_tag=0;
}tree[400100];
int a[100100];

void build(int x,int l,int r)
{
	tree[x].l=l;
	tree[x].r=r;
	tree[x].lazy_tag=0;
	if(l==r)
	{
		tree[x].value=0;
		tree[x].sum=a[l];
		return ;
	}
	int mid=(l+r)/2;
	build(x*2,l,mid);
	build(x*2+1,mid+1,r);
	
	tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
	tree[x].value=tree[x*2].value+tree[x*2+1].value+tree[x*2].sum*tree[x*2+1].sum;
	
	tree[x].sum%=mod;
	tree[x].value%=mod;
}

void update(int x,int w)
{
	int len=tree[x].r-tree[x].l+1;
	tree[x].value += ( (((w*w)%mod)*((len*(len-1)/2)%mod)+(((w*(len-1))%mod)*tree[x].sum)%mod)%mod );
	tree[x].sum += len*w;tree[x].sum%=mod;
	tree[x].lazy_tag+=w;tree[x].lazy_tag%=mod;
	return ;
}
void push_down(int x)
{
	update(x*2,tree[x].lazy_tag);
	update(x*2+1,tree[x].lazy_tag);
	tree[x].lazy_tag=0;
}

void push_up(int x)
{
	tree[x].sum=(tree[x*2].sum+tree[x*2+1].sum)%mod;
	tree[x].value=((tree[x*2].value+tree[x*2+1].value)%mod+((tree[x*2].sum*tree[x*2+1].sum)%mod)%mod)%mod;
}

void change(int x,int l,int r,int w)
{
	if(tree[x].l==l&&tree[x].r==r)
	{
		update(x,w);
		return ;
	}
	
	if(tree[x].lazy_tag!=-1) push_down(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(r<=mid) change(x*2,l,r,w);
	else if(l>=mid+1) change(x*2+1,l,r,w);
	else
	{
		change(x*2,l,mid,w);
		change(x*2+1,mid+1,r,w);
	}
	push_up(x);
}
 
pair<int,int> query(int x,int l,int r)
{
	if(tree[x].l==l&&tree[x].r==r)
	{
		return make_pair(tree[x].value,tree[x].sum);
	}
	
	if(tree[x].lazy_tag!=0) push_down(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(r<=mid)
	{
		return query(x*2,l,r);
	}
	else if(l>=mid+1)
	{
		return query(x*2+1,l,r);
	}
	else
	{
		pair<int,int> oxy=query(x*2,l,mid);
		pair<int,int> orz=query(x*2+1,mid+1,r);
		return make_pair
		( (oxy.first+orz.first+(oxy.second*orz.second)%mod)%mod,(oxy.second+orz.second)%mod );
	}
} 

signed main( )
{
	int t;
	scanf("%lld",&t);
while(t--)
{
	int q;
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	build(1,1,n);
	
	while(q--)
	{
		int op,l,r;
		scanf("%lld%lld%lld",&op,&l,&r);
		if(op==1)//区间修改 
		{
			int v;
			scanf("%lld",&v);
			change(1,l,r,v);
		}
		else if(op==2)//区间查询 
		{
			int ans=query(1,l,r).first;
			ans=(ans%mod+mod)%mod;
			printf("%lld\n",ans);
		}
		
	}
	
}
return 0;
}
这篇关于队内训练5 线段树魔改的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!