Description

Solution

有两种做法

一种是线段树维护一次方程系数,一种是线段树维护矩阵

准备都写一写


维护系数

首先把式子推出来 $$CS=B\times \sum\limits_{i=1}^np_i\times(f_{i-1}+1)$$

\[f_i=(t+p_i-p_i\times t)\times f_{i-1}+p_i
\]

发现 \(f_i\) 是关于 \(f_{i-1}\) 的一次函数 \(y=kx+b\) 形式,可以线段树维护这个 \(k\) 和 \(b\)。

还有 \(p_i\times(f_{i-1}+1)=p_i\times f_{i-1}+p_i\) 也是个一次函数,也能用线段树维护。

具体都维护些什么呢?

在区间 \([L,R]\) 维护 \(k_1,b_1\) 表示 \(f_R=k_1f_{L-1}+b_1\) 和 \(k_2,b_2\) 表示 \(CS[L,R]=k_2f_{L-1}+b_2\)

观察到这样做的好处就是右区间的 \(L-1\) 等于左区间的 \(R\),这样就资瓷快速合并了。

然后如果一个询问是 \([l,r]\),那求出来的 \(CS[L,R]\) 的 \(b_2\) 就是答案了。

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
const int N=500005;
typedef long long ll;
const int mod=998244353;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define mp(A,B) std::make_pair(A,B)
#define ls cur<<1
#define rs cur<<1|1
#define lss ls,l,mid,ql,qr
#define rss rs,mid+1,r,ql,qr int n,q,t,tb,A,B,b1[N<<2],b2[N<<2];
int p[N],sump[N<<2],k1[N<<2],k2[N<<2]; struct Node{
int sump,k1,b1,k2,b2;
friend Node operator+(Node x,Node y){
Node z;
z.sump=(x.sump+y.sump)%mod;
z.k1=(ll)y.k1*x.k1%mod;
z.b1=((ll)y.k1*x.b1%mod+y.b1)%mod;
z.k2=((ll)y.k2*x.k1%mod+x.k2)%mod;
z.b2=((ll)y.k2*x.b1%mod+y.b2+x.b2)%mod;
return z;
}
}; int getint(){
int X=0,w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
} int ksm(int x,int y){
int ans=1;
while(y){
if(y&1) ans=(ll)ans*x%mod;
x=(ll)x*x%mod;y>>=1;
} return ans;
} int inv(int x){
return ksm(x,mod-2);
} void pushup(int cur){
k1[cur]=(ll)k1[rs]*k1[ls]%mod;
b1[cur]=((ll)k1[rs]*b1[ls]%mod+b1[rs])%mod;
k2[cur]=((ll)k2[rs]*k1[ls]%mod+k2[ls])%mod;
b2[cur]=((ll)k2[rs]*b1[ls]%mod+(ll)b2[rs]+b2[ls])%mod;
sump[cur]=((ll)sump[ls]+sump[rs])%mod;
} void build(int cur,int l,int r){
if(l==r){
k1[cur]=((ll)t+p[l]-(ll)p[l]*t%mod+mod)%mod;
b1[cur]=p[l];
k2[cur]=p[l];
b2[cur]=p[l];
sump[cur]=p[l];
return;
} int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(cur);
} void modify(int cur,int l,int r,int ql,int qr,int c){
if(l==r){
k1[cur]=((ll)t+c-(ll)c*t%mod+mod)%mod;
b1[cur]=k2[cur]=b2[cur]=sump[cur]=c;
return;
} int mid=l+r>>1;
ql<=mid?modify(lss,c):modify(rss,c);
pushup(cur);
} Node query(int cur,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return (Node){sump[cur],k1[cur],b1[cur],k2[cur],b2[cur]};
int mid=l+r>>1;
if(qr<=mid) return query(lss);
if(ql>mid) return query(rss);
return query(lss)+query(rss);
} signed main(){
freopen("omeed.in","r",stdin);freopen("omeed.out","w",stdout);
getint();n=getint(),q=getint(),t=getint(),tb=getint(),A=getint(),B=getint();
t=(ll)t*inv(tb)%mod;
for(int i=1;i<=n;i++){
int x=getint(),y=getint();
p[i]=(ll)x*inv(y)%mod;
} build(1,1,n);
while(q--){
if(getint()==0){
int pos=getint(),a=getint(),b=getint(),c=(ll)a*inv(b)%mod;
p[pos]=c;modify(1,1,n,pos,pos,c);
} else{
int l=getint(),r=getint();
Node ans=query(1,1,n,l,r);
printf("%d\n",((ll)ans.sump*A%mod+(ll)ans.b2*B%mod)%mod);
}
} return 0;
}

维护矩阵

上面的DP式子已经列出来了。

我们现在要解决的这样一类问题:我们知道DP式子,现在要求多次从不同的起点开始做DP

求出DP的转移矩阵然后线段树上动态DP就好了

姿势不对会T一个点懒得卡常了

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
const int N=500005;
const int mod=998244353;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<Mat,int>
#define mp(A,B) std::make_pair(A,B)
#define ls cur<<1
#define rs cur<<1|1
#define lss ls,l,mid,ql,qr
#define rss rs,mid+1,r,ql,qr int n,q,t,ta,A,B;
int p[N],sum[N<<2]; struct Mat{
int a[4][4]; void clear(){
memset(a,0,sizeof a);
} void init(){
clear();
for(int i=1;i<=3;i++) a[i][i]=1;
} friend Mat operator*(Mat x,Mat y){
Mat z;z.clear();
for(int i=1;i<=3;i++)
for(int k=1;k<=3;k++)
for(int j=1;j<=3;j++)
z.a[i][j]=((ll)z.a[i][j]+(ll)x.a[i][k]*y.a[k][j]%mod)%mod;
return z;
}
}cs[N<<2]; void pushup(int cur){
cs[cur]=cs[ls]*cs[rs];
sum[cur]=(sum[ls]+sum[rs])%mod;
} void build(int cur,int l,int r){
if(l==r){
cs[cur].a[1][1]=((ll)p[l]+t-(ll)p[l]*t%mod+mod)%mod;cs[cur].a[1][2]=(ll)B*p[l]%mod;cs[cur].a[1][3]=0;
cs[cur].a[2][1]=0;cs[cur].a[2][2]=1;cs[cur].a[2][3]=0;sum[cur]=p[l];
cs[cur].a[3][1]=p[l];cs[cur].a[3][2]=(ll)p[l]*(B+A)%mod;cs[cur].a[3][3]=1;
return;
} int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);pushup(cur);
} void modify(int cur,int l,int r,int ql,int qr,int c){
if(l==r){
cs[cur].a[1][1]=((ll)c+t-(ll)c*t%mod+mod)%mod;cs[cur].a[1][2]=(ll)B*c%mod;cs[cur].a[1][3]=0;
cs[cur].a[2][1]=0;cs[cur].a[2][2]=1;cs[cur].a[2][3]=0;sum[cur]=c;
cs[cur].a[3][1]=c;cs[cur].a[3][2]=(ll)c*(B+A)%mod;cs[cur].a[3][3]=1;return;
} int mid=l+r>>1;
ql<=mid?modify(lss,c):modify(rss,c);
pushup(cur);
} Mat query(int cur,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return cs[cur];
int mid=l+r>>1;
if(qr<=mid) return query(lss);
if(ql>mid) return query(rss);
return query(lss)*query(rss);
} int getint(){
int X=0,w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
} int ksm(int a,int y){
int ans=1;
while(y){
if(y&1) ans=(ll)ans*a%mod;
a=(ll)a*a%mod;y>>=1;
} return ans;
} int inv(int x){
return ksm(x,mod-2);
} signed main(){
getint(),n=getint(),q=getint(),t=getint(),ta=getint(),A=getint(),B=getint();
t=(ll)t*inv(ta)%mod;
for(int i=1;i<=n;i++){
int x=getint(),y=getint();
p[i]=(ll)x*inv(y)%mod;
} build(1,1,n);
while(q--){
if(getint()==0){
int pos=getint(),x=getint(),y=getint(),z=(ll)x*inv(y)%mod;
modify(1,1,n,pos,pos,z);
} else{
int l=getint(),r=getint();
printf("%d\n",query(1,1,n,l,r).a[3][2]);
}
} return 0;
}

[JZOJ5837] Omeed的更多相关文章

  1. [JZOJ] 5837.Omeed

    先摆出来这个式子 \[ score=A\sum S_i+B\sum S_i\times f(i) \] 先研究\(f\)函数(也就是Combo函数) 显然的有 \[ f(i)=P_i(f(i-1)+1 ...

  2. 20210824 Prime,Sequence,Omeed

    考场 T1 貌似是 luogu 上原题 T2 计数,想起了这题和这题,但没有 \(n^2\) 一档的分...准备打个表 T3 期望 DP,但暴力是 \(O(qn)\) 的,发现 \(combo\) 的 ...

  3. Omeed 线段树

    目录 题面 题解 代码 题面 2.12 - - - 题解 大概还是挺妙的? 首先基础分和连击分互不干扰,所以可以分开统计. 基础分的统计比较简单,等于: \[A \sum_{i = l}^{r} p_ ...

  4. 题解 Omeed

    传送门 差了一点没想到正解-- 首先单次询问的 \(O(n)\) 写法很好想,考虑如何优化 首先基础分区间求和即可 然后那个连击分的话,是一个关于 \(f_i\) 和 \(f_{i-1}\) 的柿子 ...

  5. [考试总结]noip模拟47

    感觉自己放弃题目还是过于容易. 其实第一题不是很难,但是自己拿了一个暴力就走人了.. 然后其实简单优化一下子就有不少分数. 然后第二题的本质不同的子序列个数的方程没有推出来,如果推出来就会直接有 \( ...

  6. Noip模拟47 2021.8.25

    期望得分:55+24+53 实际得分:0+0+3 乐死 累加变量清零了吗? 打出更高的部分分暴力删了吗? 样例解释换行你看见了吗? T1 Prime 打出55分做法没删原来的暴力,结果就轻松挂55分 ...

  7. 2021.8.24考试总结[NOIP47]

    T1 prime 发现只需筛小于等于$mid(\sqrt r,k)$的质数,之后用这些质数筛掉区间内不合法的数即可. $code:$ 1 #include<bits/stdc++.h> 2 ...

随机推荐

  1. 描述linux下文件删除的原理

    Linux文件删除原理: Linux是通过link的数量来控制文件删除的,只有当一个文件不存在任何link的时候,这个文件才会被删除. 一般来说,每个文件都有2个link计数器:i_count 和 i ...

  2. 如何在html与delphi间交互代码

    [转]如何在html与delphi间交互代码 (2015-11-19 22:16:24) 转载▼ 标签: it 分类: uniGUI uniGUI总群中台中cmj朋友为我们总结了如下内容,对于利用de ...

  3. bash编程-grep

    grep, egrep, fgrep :输出匹配模式的行 grep:支持基本正则表达式元字符(grep -E相当于egrep) egrep:支持扩展正则表达式元字符(egrep -G相当于grep) ...

  4. shell脚本基础教程

    一.什么是shell: shell解释:引用别人的话说:“Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言.” 简而言之, ...

  5. NET Core微服务之路:再谈分布式系统中一致性问题分析

    前言 一致性:很多时候表现在IT系统中,通常在分布式系统中,必须(或最终)为多个节点的数据保持一致.世间万物,也有存在相同的特征或相似,比如儿时的双胞胎,一批工厂流水线的产品,当然,我们不去讨论非IT ...

  6. 我知道的nginx配置

    1.nginx配置文件 2.配置访问域名 #京淘商品管理系统 server { listen 80; server_name manage.jt.com; location / { proxy_pas ...

  7. 响应式网站设计(Responsive Web design)

    页面的设计与开发应当根据用户行为以及设备环境(系统平台.屏幕尺寸.屏幕定向等)进行相应的响应和调整.具体的实践方式由多方面组成,包括弹性网格和布局.图片.CSS media query的使用等.无论用 ...

  8. 在Java Web中使用Spark MLlib训练的模型

    PMML是一种通用的配置文件,只要遵循标准的配置文件,就可以在Spark中训练机器学习模型,然后再web接口端去使用.目前应用最广的就是基于Jpmml来加载模型在javaweb中应用,这样就可以实现跨 ...

  9. 【详细】Android入门到放弃篇-YES OR NO-》各种UI组件,布局管理器,单元Activity

    问:达叔,你放弃了吗? 答:不,放弃是不可能的,丢了Android,你会心疼吗?如果别人把你丢掉,你是痛苦呢?还是痛苦呢?~ 引导语 有人说,爱上一个人是痛苦的,有人说,喜欢一个人是幸福的. 人与人之 ...

  10. Swift5 语言指南(二) 版本兼容性

    本书描述了Swift 5,它是Xcode 10.2中包含的Swift的默认版本.您可以使用Xcode 10.2构建以Swift 5,Swift 4.2或Swift 4编写的目标. 当您使用Xcode ...