码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ 2962 序列操作 线段树

时间:2015-02-28 18:42:53      阅读:168      评论:0      收藏:0      [点我收藏+]

标签:bzoj   bzoj2962   线段树   

题目大意:给定一个序列,给定一个长度为n的序列,维护三种操作:

区间加

区间变为相反数

求某个区间内任取c个不同的数乘积的所有方案之和对P的模

比如说a b c三个数中取两个 就是ab+ac+bc

这题显然是用线段树来维护下- -

我们用一个0~20的数组a来记录某个区间的信息,其中a[i]表示区间内取i个数的乘积之和

区间合并就是a[i]=Σb[j]*c[i-j] 这个很好理解

区间变为相反数就是把a[1],a[3],a[5],...,a[19]都取反

区间加有点麻烦 就是把a*b*c变成(a+x)*(b+x)*(c+x) 然后把这个式子展开得到a‘[i]=Σa[i-j]*(x^j)*C(len-i+j,j)

其中len是区间长度

然后就可以了 时间复杂度O(nlognc^2)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
#define MOD 19940417
using namespace std;
int C[M][25];
struct abcd{
	int a[21],size;
	//a[i]表示此区间内选i个数乘积之和
	abcd()
	{
		memset(a,0,sizeof a);size=0;
	}
	abcd(int x)
	{
		size=1;a[0]=1;a[1]=x;
	}
	int& operator [] (int x)
	{
		return a[x];
	}
	friend abcd operator + (abcd x,abcd y)
	{
		int i,j;abcd re;
		for(i=0;i<=20;i++)
			for(j=0;j<=i;j++)
				(re[i]+=(long long)x[j]*y[i-j]%MOD)%=MOD;
		re.size=x.size+y.size;
		return re;
	}
	void operator += (int x)
	{
		int i,j,temp=x;
		for(i=20;~i;i--,temp=x)
			for(j=1;j<=i;j++,temp=(long long)temp*x%MOD)
				(a[i]+=(long long)a[i-j]*temp%MOD*C[size-i+j][j]%MOD)%=MOD;
	}
	void Negate()
	{
		int i;
		for(i=1;i<=20;i+=2)
			a[i]=(MOD-a[i])%MOD;
	}
};
struct Segtree{
	Segtree *ls,*rs;
	abcd val;
	int add_mark;
	bool neg_mark;
	Segtree()
	{
		ls=rs=0x0;
		add_mark=0;
		neg_mark=false;
	}
	void Build_Tree(int x,int y,int a[])
	{
		int mid=x+y>>1;
		if(x==y)
		{
			new(&val)abcd(a[mid]);
			return ;
		}
		(ls=new Segtree)->Build_Tree(x,mid,a);
		(rs=new Segtree)->Build_Tree(mid+1,y,a);
		val=ls->val+rs->val;
	}
	void Add(int x)
	{
		(add_mark+=x)%=MOD;
		val+=x;
	}
	void Negate()
	{
		add_mark=(MOD-add_mark)%MOD;
		neg_mark^=1;
		val.Negate();
	}
	void Push_Down()
	{
		if(neg_mark)
		{
			ls->Negate();
			rs->Negate();
			neg_mark=false;
		}
		if(add_mark)
		{
			ls->Add(add_mark);
			rs->Add(add_mark);
			add_mark=0;
		}
	}
	void Add(int x,int y,int l,int r,int val)
	{
		int mid=x+y>>1;
		if(l==x&&y==r)
		{
			Add(val);
			return ;
		}
		Push_Down();
		if(r<=mid)
			ls->Add(x,mid,l,r,val);
		else if(l>mid)
			rs->Add(mid+1,y,l,r,val);
		else
			ls->Add(x,mid,l,mid,val) , rs->Add(mid+1,y,mid+1,r,val);
		this->val=ls->val+rs->val;
	}
	void Negate(int x,int y,int l,int r)
	{
		int mid=x+y>>1;
		if(x==l&&y==r)
		{
			Negate();
			return ;
		}
		Push_Down();
		if(r<=mid)
			ls->Negate(x,mid,l,r);
		else if(l>mid)
			rs->Negate(mid+1,y,l,r);
		else
			ls->Negate(x,mid,l,mid) , rs->Negate(mid+1,y,mid+1,r);
		this->val=ls->val+rs->val;
	}
	abcd Get_Ans(int x,int y,int l,int r)
	{
		int mid=x+y>>1;
		if(x==l&&y==r)
			return val;
		Push_Down();
		if(r<=mid)
			return ls->Get_Ans(x,mid,l,r);
		if(l>mid)
			return rs->Get_Ans(mid+1,y,l,r);
		return ls->Get_Ans(x,mid,l,mid) + rs->Get_Ans(mid+1,y,mid+1,r);
	}
}tree;

int n,q;

void Pretreatment()
{
	int i,j;
	for(i=0;i<=n;i++)
		for(C[i][0]=1,j=1;j<=i&&j<=20;j++)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}

int main()
{
	int i,x,y,z;
	cin>>n>>q;
	char p[10];
	Pretreatment();
	static int a[M];
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]),a[i]=(a[i]%MOD+MOD)%MOD;
	tree.Build_Tree(1,n,a);
	for(i=1;i<=q;i++)
	{
		scanf("%s%d%d",p,&x,&y);
		if(p[0]=='I')
			scanf("%d",&z),z=(z%MOD+MOD)%MOD,tree.Add(1,n,x,y,z);
		else if(p[0]=='R')
			tree.Negate(1,n,x,y);
		else
			scanf("%d",&z),printf("%d\n",tree.Get_Ans(1,n,x,y)[z]);
	}
	return 0;
}


BZOJ 2962 序列操作 线段树

标签:bzoj   bzoj2962   线段树   

原文地址:http://blog.csdn.net/popoqqq/article/details/43987881

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!