zoukankan      html  css  js  c++  java
  • 【洛谷4247】[清华集训2012] 序列操作(线段树)

    点此看题面

    • 给定一个长度为(n)的序列,要求支持三种操作:
      • 给一段区间加上一个数。
      • 将一段区间的数变成它们的相反数。
      • 询问从这段区间选(k)个数相乘的所有积之和。
    • (n,qle5 imes10^4,kle20)

    线段树基础思路

    这种选若干数相乘的所有积显然不太好求,而(k)这么小就是在明示我们要直接对于所有的(k)维护答案。

    所以一个基础思路就是对于每个节点维护好这个区间内的所有答案。

    而区间之间答案的合并是非常简单的,只要枚举从左区间选择(i)个数,右区间选择(j)个数,合在一起就是(i+j)个数,类似于一个卷积的形式。

    针对修改的处理

    首先,容易发现区间取反其实就是让选奇数个数的答案变成其相反数,选偶数个数的答案不变。

    那么核心问题就在于如何解决给一段区间加上一个数(v)的问题。

    我们枚举(i,j),考虑(v^{i-j})对于(i)的答案的贡献,那么能与之配成乘积的所有积之和就应该是(f_j),而方案数就应该是(C_{L-j}^{i-j})(其中(L)为区间大小,就表示从剩余数中选出这(i-j)个数的方案数)。

    于是这道题就做完了。

    代码:(O(nlognk^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50000
    #define K 20
    #define X 19940417
    using namespace std;
    int n,a[N+5],C[N+5][K+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	#define D isdigit(oc=tc())
    	int Ff,OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0,Ff=1;W(!D) Ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),D);x*=Ff;}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	I void readc(char& x) {W(isspace(x=tc()));}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    struct Data
    {
    	int L,f[K+5];I Data() {L=0,memset(f,0,sizeof(f));}
    	I int& operator [] (CI x) {return f[x];}I int operator [] (CI x) Con {return f[x];}
    	I Data operator + (Con Data& o) Con {Data t;RI i,j;t.L=L+o.L;//合并区间
    		for(i=0;i<=K;++i) for(j=0;j<=K-i;++j) t[i+j]=(1LL*f[i]*o[j]+t[i+j])%X;return t;}//枚举两边各选多少数
    	I Data operator + (CI v) Con {Data t;RI i,j,p;for(t[0]=1,t.L=L,i=1;i<=min(L,K);++i)//给所有数加v
    		 for(p=1,j=i;~j;--j,p=1LL*p*v%X) t[i]=(1LL*p*f[j]%X*C[L-j][i-j]+t[i])%X;return t;}//枚举考虑v^(i-j)对f[i]的贡献
    	I friend Data operator ~ (Data x) {for(RI i=1;i<=K;i+=2) x[i]=(X-x[i])%X;return x;}//取反,只对于奇数位
    };
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define PU(x) (O[x].V=O[x<<1].V+O[x<<1|1].V)//合并区间
    		#define PD(x) (O[x].P&&(OP(x<<1),OP(x<<1|1),O[x].P=0),O[x].F&&(T(x<<1,O[x].F),T(x<<1|1,O[x].F),O[x].F=0))//下推标记
    		#define T(x,v) (O[x].V=O[x].V+v,O[x].F=(O[x].F+v)%X)//打上加法标记
    		#define OP(x) (O[x].V=~O[x].V,O[x].F=(X-O[x].F)%X,O[x].P^=1)//打上取反标记,注意把加法标记也取反
    		struct node {Data V;int P,F;}O[N<<2];
    	public:
    		I void Build(PT)//建树
    		{
    			if(l==r) return (void)(O[rt].V[0]=1,O[rt].V[1]=a[l],O[rt].V.L=1);
    			RI mid=l+r>>1;Build(LT),Build(RT),PU(rt);
    		}
    		I void A(CI L,CI R,CI v,PT)//区间加法
    		{
    			if(L<=l&&r<=R) return (void)T(rt,v);PD(rt);
    			RI mid=l+r>>1;L<=mid&&(A(L,R,v,LT),0),R>mid&&(A(L,R,v,RT),0),PU(rt);
    		}
    		I void U(CI L,CI R,PT)//区间取反
    		{
    			if(L<=l&&r<=R) return (void)OP(rt);PD(rt);
    			RI mid=l+r>>1;L<=mid&&(U(L,R,LT),0),R>mid&&(U(L,R,RT),0),PU(rt);
    		}
    		I Data Q(CI L,CI R,PT)//区间询问
    		{
    			if(L==l&&r==R) return O[rt].V;PD(rt);RI mid=l+r>>1;
    			if(R<=mid) return Q(L,R,LT);if(L>mid) return Q(L,R,RT);return Q(L,mid,LT)+Q(mid+1,R,RT);
    		}
    }S;
    int main()
    {
    	RI i,j,Qt;for(read(n,Qt),i=1;i<=n;++i) read(a[i]),a[i]<0&&(a[i]+=X);
    	for(C[0][0]=i=1;i<=n;++i) for(C[i][0]=j=1;j<=min(i,K);++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X;//预处理组合数
    	char op;RI l,r,x;S.Build();W(Qt--) switch(readc(op),read(l,r),op)
    	{
    		case 'I':read(x),x<0&&(x+=X),S.A(l,r,x);break;case 'R':S.U(l,r);break;
    		case 'Q':read(x),writeln(S.Q(l,r)[x]);break;
    	}return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    NGINX_深度优化实践
    NFS服务端___NFS客户端
    NFS 批量管理 分发
    MYSQL---数据备份与还原
    MYSQL---建立字符集数据库
    MYSQL---基于mysql多实例数据库创建主从复制
    MYSQL---关于MYSQL优化
    bug记录-left jion连接后不是一对一情况时,记得去重
    bug记录-不等于某个值,查询后注意不包括为空的情况(由于NULL不能直接用算术运算符进行比较值。要想把为NULL 的那行也查询出来的话,只能使用IS NULL)
    bug记录-sqljion连接 like
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4247.html
Copyright © 2011-2022 走看看