#164. 【清华集训2015】V
Picks博士观察完金星凌日后,设计了一个复杂的电阻器。为了简化题目,题目中的常数与现实世界有所不同。
这个电阻器内有编号为 1∼n1∼n 的 nn 个独立水箱,水箱呈圆柱形,底面积为 1 m21 m2,每个水箱在顶部和底部各有一个阀门,可以让水以 1 m3/s 的流量通过,每个水箱的上阀门接水龙头,可以无限供应水,下阀门不接东西,可以让水流出。水箱顶部和底部都有一个接口,水的电阻率为 1 Ω⋅m。
水箱的高度足够高,有一个导电浮标浮在水面上,通过导线与水箱顶的接口相连。一开始时第 ii 个水箱中有 ai m3 的水。
Picks博士接下来就需要对这个复杂的电阻器进行调试。他会进行以下五种操作。
1、打开编号在 [l,r][l,r] 中的所有水箱的上方阀门 xx 秒,然后关上它们的上方阀门。
2、打开编号在 [l,r][l,r] 中的所有水箱的下方阀门 xx 秒,然后关上它们的下方阀门。
3、将编号在 [l,r][l,r] 中的所有水箱的下方阀门与大海通过连通器以一定方式相连,使得这些水箱中都恰拥有 x m3x m3 的水,然后关上它们的下方阀门,撤去连通器。
4、在第 yy 个水箱的上下方接口处接上一个电动势为 1 V1 V 的电源,电源没有内阻,Picks博士会测量出通过电源的电流大小,之后撤去该电源。
5、由于水浸泡过的地方会留下明显的水渍而没有被水浸泡过的地方不会有,Picks博士可以据此测量出此时第 yy 个水箱的水渍高度,以推断曾经最多有多少水,节约他的建造成本。
现在,他请你来帮他做预实验,你能告诉他每次测量得到的电流大小以及测量得到的最多的水量是多少吗?
输入格式
第一行两个数:n,mn,m。
接下来一行 nn 个数,第 ii 个数表示初始时第 ii 个水箱内有 ai m3ai m3 的水。
接下来 mm 行中,第 ii 行第一个数 titi 表示操作类型:
若 ti=1ti=1,则接下来三个整数 li,ri,xili,ri,xi,表示打开编号在 [li,ri][li,ri] 中的所有水箱的上方接口 xixi 秒。
若 ti=2ti=2,则接下来三个整数 li,ri,xili,ri,xi,表示打开编号在 [li,ri][li,ri] 中的所有水箱的下方接口 xixi 秒。
若 ti=3ti=3,则接下来三个整数 li,ri,xili,ri,xi,表示将编号在 [li,ri][li,ri] 中的所有水箱与大海连接,使这些水箱中都恰有 xi m3xi m3 的水。
若 ti=4ti=4,则接下来一个整数 yiyi,表示测量在第 yiyi 个水箱的上下方接口处接上一个电动势为 1 V1 V 的电源时通过电源的电流。
若 ti=5ti=5,则接下来一个整数 yiyi,表示测量此时在第 yiyi 个水箱中的水渍高度。
输出格式
对于每个 ti=4ti=4,输出一个整数表示通过电源的电流大小的倒数(单位为 A−1A−1 ),如果电流为无穷大则输出0。
对于每个 ti=5ti=5,输出一个整数表示在第 yiyi 个水箱中的水渍高度(单位为 mm )。
样例输入一
5 6 1 2 3 4 5 2 1 3 2 4 1 1 1 4 1 5 3 3 1 5 4 4 2
样例输出一
0 3 4
样例输入二
见相关文件下载
样例输出二
见相关文件下载
样例输入三
见相关文件下载
样例输出三
见相关文件下载
限制与约定
时间限制:2s
空间限制:128MB
测试点编号 | n=n= | m=m= | 约定 |
---|---|---|---|
1 | 10001000 | 10001000 | |
2 | 10001000 | 10001000 | |
3 | 105105 | 105105 | 没有操作2 |
4 | 5×1055×105 | 5×1055×105 | 没有操作2 |
5 | 105105 | 105105 | 没有操作1与操作5 |
6 | 105105 | 105105 | 没有操作1 |
7 | 5×1055×105 | 5×1055×105 | 没有操作1 |
8 | 5×1055×105 | 5×1055×105 | 没有操作5 |
9 | 105105 | 105105 | |
10 | 5×1055×105 | 5×1055×105 |
对于所有的数据:1≤n,m≤5×105, 0≤ai,xi≤109,1≤li≤ri≤n, 1≤yi≤n1≤n,m≤5×105, 0≤ai,xi≤109,1≤li≤ri≤n, 1≤yi≤n.
提示
可能用到的物理公式:
1、欧姆定律:I=URI=UR,其中 I,U,RI,U,R 分别代表电流、电压和电阻。
2、电阻率公式:R=ρLSR=ρLS,其中 R,ρ,L,SR,ρ,L,S 分别代表电阻、电阻率、电阻长度、横截面积。
下载
神奇的搞了一个标记(a,b),表示x=max{x+a,b}
统一了赋值、加减、查询操作,orz
区间加w:标记(w,0)
区间减w,再跟0取max:标记(-w,0)
区间赋值w:标记(-inf,w)
查询操作:维护当前的a,当前的b,历史最大的a,历史最大的b
标记的合并:
假设x原有标记(a,b),现在又来了一个标记(c,d)
原来的x=max{x+a,b}
现在x=max{x+a+c,b+c,d}=max{x+a+c,max(b+c,d)}
标记的下传:
先下传历史标记,再下传当前标记
若父节点有标记,那构成这个标记的所有操作一定在子节点标记后面,否则这个标记早就被传了下去
假设构成子节点标记的操作序列为[1,r],构成父节点标记的操作序列为[L,R],那么1<r<L<R
更新子节点历史标记:子节点的历史最大值应该在 操作序列[1,r] 和 操作序列[1,R] 中取最大
操作序列[1,r]就是子节点的标记,
操作序列[1,R]就是 标记[1,r]与标记[L,R]的合并,把[1,r]看作原有标记,把[L,R]看作又来的标记
更新子节点现有标记:两个标记直接加,然后取大即可
注意区间减w,可能会减爆设置的极小值,所以涉及到a的直接赋值操作都对-inf取max
每个点的初始标记为(0,0),每个点的初始值赋给a,那么实际代码中对标记的操作可以把x去掉
#include<cstdio> #include<algorithm> #include<iostream> #include<cmath> #define N 500001 #define inf (1LL<<62) using namespace std; int n,m; long long ansa,ansb; struct node { int l,r; long long na,nb,ha,hb; bool f; }tr[N*4]; void build(int k,int l,int r) { tr[k].l=l; tr[k].r=r; if(l==r) { scanf("%lld",&tr[k].na); tr[k].ha=tr[k].na; return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void down(int k) { for(int i=0,z;z=k<<1|i,i<2;i++) { tr[z].ha=max(tr[z].ha,tr[k].ha+tr[z].na); tr[z].hb=max(tr[z].hb,max(tr[z].nb+tr[k].ha,tr[k].hb)); tr[z].na=max(tr[z].na+tr[k].na,-inf); tr[z].nb=max(tr[z].nb+tr[k].na,tr[k].nb); } tr[k].na=tr[k].nb=tr[k].ha=tr[k].hb=0; tr[k].f=0; tr[k<<1].f=tr[k<<1|1].f=1; } void change(int k,int l,int r,long long a,long long b) { if(tr[k].l>=l&&tr[k].r<=r) { tr[k].na=max(tr[k].na+a,-inf); tr[k].nb=max(tr[k].nb+a,b); tr[k].ha=max(tr[k].ha,tr[k].na); tr[k].hb=max(tr[k].hb,tr[k].nb); tr[k].f=true; return; } if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1; if(l<=mid) change(k<<1,l,r,a,b); if(r>mid) change(k<<1|1,l,r,a,b); } void query(int k,int p,int w) { if(tr[k].l==tr[k].r) { ansa= w==4 ? tr[k].na : tr[k].ha; ansb= w==4 ? tr[k].nb : tr[k].hb; return; } if(tr[k].f) down(k); int mid=tr[k].l+tr[k].r>>1; if(p<=mid) query(k<<1,p,w); else query(k<<1|1,p,w); } int main() { scanf("%d%d",&n,&m); build(1,1,n); int op,l,r,x; while(m--) { scanf("%d",&op); if(op==1) { scanf("%d%d%d",&l,&r,&x); change(1,l,r,x,0); } else if(op==2) { scanf("%d%d%d",&l,&r,&x); change(1,l,r,-x,0); } else if(op==3) { scanf("%d%d%d",&l,&r,&x); change(1,l,r,-inf,x); } else if(op==4) { scanf("%d",&l); query(1,l,4); printf("%lld\n",max(ansa,ansb)); } else { scanf("%d",&l); query(1,l,5); printf("%lld\n",max(ansa,ansb)); } } }