SPOJ GSS系列
眾所周知的僅次于ynoi的毒瘤數據結構系列。(跟Qtree系列并列?)
GSS1:
長度為 $n$ 的序列 $a$,$m$ 個詢問,每次詢問區間 $[l,r]$ 之間的最大子段和。
$1le n,mle 5 imes 10^4$。
經典的線段樹題。
每個節點維護四個值:$sum,lmax,rmax,amax$。
$sum$ 表示整個區間的和。
$lmax$ 表示以 $l$ 為左端點的最大子段和。
$rmax$ 表示以 $r$ 為右端點的最大子段和。
$amax$ 表示整個的最大子段和。
時間復雜度 $O(mlog n)$。
具體可以看代碼。(那是好久以前寫的了,所以會有點丑)
#include<bits/stdc++.h>
using namespace std;
const int maxm=144089;
namespace Segment_Tree{
struct node{
int sum,lmax,rmax,amax;
}e[maxm];
int n;
inline void pushup(int t){
node ls=e[t<<1],rs=e[t<<1|1];
e[t]=(node){
ls.sum+rs.sum,
max(ls.lmax,ls.sum+rs.lmax),
max(rs.rmax,rs.sum+ls.rmax),
max(max(ls.amax,rs.amax),ls.rmax+rs.lmax)
};
}
void build_(int l,int r,int t){
if(l==r){
scanf("%d",&e[t].sum);
e[t]=(node){e[t].sum,e[t].sum,e[t].sum,e[t].sum};
return;
}
int mid=l+r>>1;
build_(l,mid,t<<1);
build_(mid+1,r,t<<1|1);
pushup(t);
}
void build(int x){
n=x;
build_(1,x,1);
}
node query_(int L,int R,int l,int r,int t){
if(L>=l && R<=r) return e[t];
int mid=L+R>>1;
if(mid>=r) return query_(L,mid,l,r,t<<1);
if(mid<l) return query_(mid+1,R,l,r,t<<1|1);
node ans,ls,rs;
ls=query_(L,mid,l,r,t<<1);rs=query_(mid+1,R,l,r,t<<1|1);
ans.sum=ls.sum+rs.sum;
ans.lmax=max(ls.lmax,ls.sum+rs.lmax);
ans.rmax=max(rs.rmax,rs.sum+ls.rmax);
ans.amax=max(max(ls.amax,rs.amax),ls.rmax+rs.lmax);
return ans;
}
int query(int l,int r){
return query_(1,n,l,r,1).amax;
}
}
int n,m;
int main(){
scanf("%d",&n);
Segment_Tree::build(n);
scanf("%d",&m);
for(int i=1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
printf("%d
",Segment_Tree::query(l,r));
}
}
GSS1
GSS2:
長度為 $n$ 的序列 $a$,$m$ 個詢問,每次詢問區間 $[l,r]$ 之間的最大子段和。可以選空子段。注意此處的子段和是:如果一個數出現了多次,那么只算一次。
$1le n,mle 10^5$。
這就搖身一變變成了毒瘤題……ErkkiErkkoOrz……(這是他洛谷賬號)
一個數只算一次,有點像HH的項鏈,那么也可以用類似的想法。
把詢問離線,按右端點排序。
假設現在推右端點推到了 $i$。
線段樹維護的東西得修改一下。(具體原因待會解釋)
每個點 $sum,hmax$。標記 $tag,htag$。
對于葉子結點 $l$,$sum$ 表示從 $l$ 到 $i$ 的和(也是相同的數只算一次),$hmax$ 表示這個節點 $sum$ 曾達到過的最大值,初始為 $0$。
對于非葉子結點,$sum$ 是所有孩子的 $sum$ 的最大值,$hmax$ 是所有孩子的 $hmax$ 的最大值。
$tag$ 是因為下面需要區間加的標記,$htag$ 是自上次把這個節點的標記下傳完以后,$tag$ 達到過的最大值。
有點不好說啊……
那么 $pre[a[i]]$(就是 $a[i]$ 上一次出現的位置)和之前的位置到 $i$ 的和應該和到 $i-1$ 的和一樣不變,因為前面已經出現過 $a[i]$,現在這個 $a[i]$ 就不能再算一遍。
$pre[a[i]]$ 之后的到 $i-1$ 的和由于沒有出現過 $a[i]$,所以他們的和就要加 $a[i]$。即給 $sum$ 區間加 $a[i]$。
接下來查詢就好辦了,就是這個區間 $hmax$ 的最大值。(想一想,為什么?)
時間復雜度 $O(m(log n+log m))$。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100010;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
struct query{
int l,r,id;
bool operator<(const query &q)const{
return r<q.r;
}
}qqq[maxn];
int n,q,pre[maxn*2],a[maxn];
ll ans[maxn],sum[maxn*4],hmax[maxn*4],tag[maxn*4],htag[maxn*4];
void pushup(int o){
sum[o]=max(sum[o<<1],sum[o<<1|1]);
hmax[o]=max(hmax[o<<1],hmax[o<<1|1]);
}
void pushdown(int o){
hmax[o<<1]=max(hmax[o<<1],sum[o<<1]+htag[o]);
hmax[o<<1|1]=max(hmax[o<<1|1],sum[o<<1|1]+htag[o]);
sum[o<<1]+=tag[o];
sum[o<<1|1]+=tag[o];
htag[o<<1]=max(htag[o<<1],tag[o<<1]+htag[o]);
htag[o<<1|1]=max(htag[o<<1|1],tag[o<<1|1]+htag[o]);
tag[o<<1]+=tag[o];
tag[o<<1|1]+=tag[o];
tag[o]=htag[o]=0;
}
void update(int o,int l,int r,int ql,int qr,int x){
if(l>=ql && r<=qr){
sum[o]+=x;
hmax[o]=max(hmax[o],sum[o]);
tag[o]+=x;
htag[o]=max(htag[o],tag[o]);
return;
}
pushdown(o);
int mid=(l+r)>>1;
if(mid>=ql) update(lson,ql,qr,x);
if(mid<qr) update(rson,ql,qr,x);
pushup(o);
}
ll query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return hmax[o];
pushdown(o);
int mid=(l+r)>>1;ll ans=-1e18;
if(mid>=ql) ans=max(ans,query(lson,ql,qr));
if(mid<qr) ans=max(ans,query(rson,ql,qr));
return ans;
}
int main(){
n=read();
FOR(i,1,n) a[i]=read();
q=read();
FOR(i,1,q) qqq[i].l=read(),qqq[i].r=read(),qqq[i].id=i;
sort(qqq+1,qqq+q+1);
int curr=1;
FOR(i,1,q){
while(curr<=qqq[i].r){
update(1,1,n,pre[a[curr]+100000]+1,curr,a[curr]);
pre[a[curr]+100000]=curr;
curr++;
}
ans[qqq[i].id]=query(1,1,n,qqq[i].l,qqq[i].r);
}
FOR(i,1,q) printf("%lld
",ans[i]);
}
GSS2
GSS3:
長度為 $n$ 的序列 $a$,$m$ 個操作:單點修改,或詢問區間的最大子段和。
$1le n,mle 5 imes 10^4$。
跟GSS1一樣,不說了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=50050;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
struct node{
int sum,lmax,rmax,amax;
}nd[maxn*4];
int n,q,a[maxn];
void pushup(node &o,node l,node r){
o.sum=l.sum+r.sum;
o.lmax=max(l.lmax,l.sum+r.lmax);
o.rmax=max(r.rmax,r.sum+l.rmax);
o.amax=max(l.amax,max(r.amax,l.rmax+r.lmax));
}
void build(int o,int l,int r){
if(l==r) return void(nd[o]=(node){a[l],a[l],a[l],a[l]});
int mid=(l+r)>>1;
build(lson);
build(rson);
pushup(nd[o],nd[o<<1],nd[o<<1|1]);
}
void update(int o,int l,int r,int p,int x){
if(l==r) return void(nd[o]=(node){x,x,x,x});
int mid=(l+r)>>1;
if(mid>=p) update(lson,p,x);
else update(rson,p,x);
pushup(nd[o],nd[o<<1],nd[o<<1|1]);
}
node query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return nd[o];
int mid=(l+r)>>1;
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
node ans;
pushup(ans,query(lson,ql,qr),query(rson,ql,qr));
return ans;
}
int main(){
n=read();
FOR(i,1,n) a[i]=read();
build(1,1,n);
q=read();
FOR(i,1,q){
int op=read(),l=read(),r=read();
if(op) printf("%d
",query(1,1,n,l,r).amax);
else update(1,1,n,l,r);
}
}
GSS3
GSS4:
長度為 $n$ 的正整數序列 $a$,$m$ 個操作:區間開方(下取整),區間求和。
$1le n,mle 10^5,sum a_ile 10^{18}$。
這應該也是一個套路了……
我們發現一個數 $x$ 開方下取整約 $loglog x$ 次之后便會變成 $1$。變成 $1$ 之后,再開方都不會對答案有影響。
(你問我為什么是 $log log x$?因為每開一次方實際上就是把 $log x$ 除以 $2$,這樣操作一共就要 $log log x$ 次)
那么就上線段樹,維護區間和和區間是否都為 $1$(bool)。
修改時,對每個葉子結點都暴力修改。注意的是如果到了一個區間,區間都是 $1$,就可以跳過這個區間,因為修不修改不影響答案。
由于一個葉子至多被暴力 $loglog a_i$ 次,所以時間復雜度是 $O(qlog nloglog a_i)$。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100010;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int t,n,q;
ll a[maxn],sum[maxn*4];
bool all1[maxn*4];
inline void pushup(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
all1[o]=all1[o<<1]&all1[o<<1|1];
}
void build(int o,int l,int r){
if(l==r) return void((sum[o]=a[l],all1[o]=(a[l]==1)));
int mid=(l+r)>>1;
build(lson);
build(rson);
pushup(o);
}
void update(int o,int l,int r,int ql,int qr){
if(all1[o]) return;
if(l==r) return void((sum[o]=sqrt(sum[o]),all1[o]=(sum[o]==1)));
int mid=(l+r)>>1;
if(mid>=ql) update(lson,ql,qr);
if(mid<qr) update(rson,ql,qr);
pushup(o);
}
ll query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return sum[o];
int mid=(l+r)>>1;ll s=0;
if(mid>=ql) s+=query(lson,ql,qr);
if(mid<qr) s+=query(rson,ql,qr);
return s;
}
int main(){
while(~scanf("%d",&n)){
printf("Case #%d:
",++t);
FOR(i,1,n) a[i]=read();
build(1,1,n);
q=read();
FOR(i,1,q){
int op=read(),l=read(),r=read();
if(l>r) swap(l,r);
if(op) printf("%lld
",query(1,1,n,l,r));
else update(1,1,n,l,r);
}
puts("");
}
}
GSS4
GSS5:
長度為 $n$ 的正整數序列 $a$,$m$ 個詢問,每次詢問左端點在 $[x1,y1]$,右端點在 $[x2,y2]$ 的所有子段的和的最大值。
$1le n,mle 10^4,x1le y1,x2le y2,x1le x2,y1le y2$。
惡心的分類討論題……
首先發現,如果 $[x1,y1],[x2,y2]$ 不相交,那么所有的子段都包含了 $[y1+1,x2-1]$(如果 $y1<x2-1$),那么答案就是 $[x1,y1]$ 的最大右子段和加上 $[x2,y2]$ 的最大左子段和加上 $[y1+1,x2-1]$ 的和。
如果相交,那就很惡心了……
如果左右端點都在 $[x2,y1]$,那么答案就是 $[x2,y1]$ 的最大子段和。
如果左端點在 $[x2,y1]$,右端點在 $[y1+1,y2]$,答案就是 $[x2,y1]$ 的最大右子段和加上 $[x2+1,y2]$ 的最大左子段和。
如果左端點在 $[x1,x2-1]$,右端點在 $[x2,y2]$,答案就是 $[x1,x2-1]$ 的最大右段和加上 $[x2,y2]$ 的最大左子段和。
以上已經包含了所有的情況。這些取個最大值就好了。
時間復雜度 $O(mlog n)$。
代碼中寫得有一點點不一樣,最重要的還是理解。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
struct node{
int sum,lmax,rmax,amax;
node operator+(const node &nd)const{
node ans;
ans.sum=sum+nd.sum;
ans.lmax=max(lmax,sum+nd.lmax);
ans.rmax=max(nd.rmax,nd.sum+rmax);
ans.amax=max(amax,max(nd.amax,rmax+nd.lmax));
return ans;
}
}nd[maxn*4];
int t,n,m,a[maxn];
inline void pushup(int o){
nd[o]=nd[o<<1]+nd[o<<1|1];
}
void build(int o,int l,int r){
if(l==r) return void(nd[o]=(node){a[l],a[l],a[l],a[l]});
int mid=(l+r)>>1;
build(lson);build(rson);
pushup(o);
}
node query(int o,int l,int r,int ql,int qr){
if(ql>qr) return (node){0,0,0,0};
if(l>=ql && r<=qr) return nd[o];
int mid=(l+r)>>1;
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
return query(lson,ql,qr)+query(rson,ql,qr);
}
int main(){
t=read();
while(t--){
n=read();
FOR(i,1,n) a[i]=read();
build(1,1,n);
m=read();
printf("m=%d
",m);
FOR(i,1,m){
int l1=read(),r1=read(),l2=read(),r2=read();
if(r1<l2){
node n1=query(1,1,n,l1,r1),n2=query(1,1,n,r1+1,l2-1),n3=query(1,1,n,l2,r2);
printf("%d
",n1.rmax+n2.sum+n3.lmax);
}
else{
node n1=query(1,1,n,l2,r1);
int ans=n1.amax;
node n2=query(1,1,n,l1,l2-1),n3=query(1,1,n,l2,r2);
ans=max(ans,n2.rmax+n3.lmax);
node n4=query(1,1,n,l1,r1),n5=query(1,1,n,r1+1,r2);
printf("%d
",max(ans,n4.rmax+n5.lmax));
}
}
}
}
GSS5
GSS6:
給出一個由 $n$個整數組成的序列 $a$,你需要支持 $m$個操作:
I p x在 $p$處插入插入一個元素 $x$
D p刪除 $p$處的一個元素
R p x修改 $p$處元素的值為 $x$
Q l r查詢一個區間 $[l,r]$的最大子段和
$1le n,mle 10^5,|a_i|,|x|le 10^4$。
這題一看就是splay(或者fhq treap)裸題。比維修數列好寫多了(
如果您打過維修數列,這個就不用再講了吧!
如果您會splay或者fhq treap,那么前三個操作應該不是問題,第四個操作也可以跟GSS1和GSS3一樣維護 $lmax,rmax,amax$。
然后就沒有了,碼吧。
#include<bits/stdc++.h>
using namespace std;
const int maxn=200020;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,a[maxn],m,cnt,rt,fa[maxn],ch[maxn][2],sz[maxn],val[maxn],sum[maxn],lmax[maxn],rmax[maxn],amax[maxn];
void pushup(int x){
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
if(!ch[x][0] && !ch[x][1]) lmax[x]=rmax[x]=amax[x]=val[x];
else if(!ch[x][1]){
lmax[x]=max(lmax[ch[x][0]],sum[ch[x][0]]+val[x]);
rmax[x]=max(val[x],rmax[ch[x][0]]+val[x]);
amax[x]=max(val[x],max(rmax[ch[x][0]]+val[x],amax[ch[x][0]]));
}
else if(!ch[x][0]){
lmax[x]=max(val[x],val[x]+lmax[ch[x][1]]);
rmax[x]=max(rmax[ch[x][1]],sum[ch[x][1]]+val[x]);
amax[x]=max(val[x],max(val[x]+lmax[ch[x][1]],amax[ch[x][1]]));
}
else{
lmax[x]=max(lmax[ch[x][0]],max(sum[ch[x][0]]+val[x],sum[ch[x][0]]+val[x]+lmax[ch[x][1]]));
rmax[x]=max(rmax[ch[x][1]],max(sum[ch[x][1]]+val[x],sum[ch[x][1]]+val[x]+rmax[ch[x][0]]));
amax[x]=max(val[x],max(amax[ch[x][0]],max(amax[ch[x][1]],max(rmax[ch[x][0]]+val[x],max(lmax[ch[x][1]]+val[x],rmax[ch[x][0]]+val[x]+lmax[ch[x][1]])))));
}
}
void rotate(int x){
int y=fa[x],z=fa[y],t=ch[y][1]==x,tt=ch[z][1]==y;
int B=ch[x][t^1];
fa[B]=y;ch[y][t]=B;
fa[y]=x;ch[x][t^1]=y;
fa[x]=z;ch[z][tt]=x;
pushup(y);pushup(x);
}
void splay(int x,int to){
while(fa[x]!=to){
int y=fa[x],z=fa[y],t=ch[y][1]==x,tt=ch[z][1]==y;
if(z!=to) rotate(t^tt?x:y);
rotate(x);
}
if(!to) rt=x;
}
int kth(int x){
int now=rt;
while(now){
int s=sz[ch[now][0]]+1;
if(x==s) return now;
if(x<s) now=ch[now][0];
else x-=s,now=ch[now][1];
}
}
int build(int f,int l,int r){
int mid=(l+r)>>1,tmp=++cnt;
fa[tmp]=f;
val[tmp]=a[mid];
if(l<mid) ch[tmp][0]=build(tmp,l,mid-1);
if(mid<r) ch[tmp][1]=build(tmp,mid+1,r);
pushup(tmp);
return tmp;
}
void insert(int p,int x){
int l=kth(p),r=kth(p+1);
splay(l,0);splay(r,l);
int &nd=ch[ch[rt][1]][0];
nd=++cnt;
fa[nd]=ch[rt][1];
val[nd]=x;
pushup(nd);pushup(ch[rt][1]);pushup(rt);
}
void remove(int p){
int l=kth(p),r=kth(p+2);
splay(l,0);splay(r,l);
ch[ch[rt][1]][0]=0;
pushup(ch[rt][1]);pushup(rt);
}
void replace(int p,int x){
int l=kth(p),r=kth(p+2);
splay(l,0);splay(r,l);
val[ch[ch[rt][1]][0]]=x;
pushup(ch[ch[rt][1]][0]);pushup(ch[rt][1]);pushup(rt);
}
int query(int l,int r){
l=kth(l);r=kth(r+2);
splay(l,0);splay(r,l);
return amax[ch[ch[rt][1]][0]];
}
int main(){
n=read();
FOR(i,1,n) a[i]=read();
rt=build(0,0,n+1);
m=read();
FOR(i,1,m){
char op[5];
scanf("%s",op);
int x=read();
switch(op[0]){
case 'I':insert(x,read());break;
case 'D':remove(x);break;
case 'R':replace(x,read());break;
case 'Q':printf("%d
",query(x,read()));break;
}
}
}
GSS6
GSS7:
給定一棵樹,有 $n$個節點,每一個節點都有一個權值 $x_i$。
你需要執行 $q$次操作:
1 a b查詢 $(a,b)$ 這條鏈上的最大子段和,可以為空
2 a b c將$(a,b)$ 這條鏈的所有點權變為 $c$
$1le n,qle 10^5,|x_i|,|c|le 10^4$。
裸的樹剖,只不過查詢時會比較麻煩。
我們可以先求出 $lca$ 到 $a$ 的最大子段和和 $lca$ 到 $b$ 的最大子段和。
具體求的話,就是可以將在上面的一條重鏈與下面已有的答案接起來。
最后把兩個合并一下就好了。注意其中一個需要翻轉后再拼。
具體看代碼。(一個裸的樹剖為什么比splay還長)
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010,INF=2147483647;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,m,w[maxn],el,head[maxn],to[maxn*2],nxt[maxn*2];
int fa[maxn],dep[maxn],sz[maxn],son[maxn],top[maxn],dfs_clock,id[maxn],dfn[maxn];
int tag[maxn*4];
struct node{
int sum,lmax,rmax,amax;
node():sum(0),lmax(0),rmax(0),amax(0){}
const node operator+(const node &r)const{
node ans;
ans.sum=sum+r.sum;
ans.lmax=max(lmax,sum+r.lmax);
ans.rmax=max(r.rmax,r.sum+rmax);
ans.amax=max(amax,max(r.amax,rmax+r.lmax));
return ans;
}
}nd[maxn*4];
inline void add(int u,int v){
to[++el]=v;nxt[el]=head[u];head[u]=el;
}
void dfs1(int u,int f){
dep[u]=dep[fa[u]=f]+1;
sz[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int topf){
top[u]=topf;
id[dfn[u]=++dfs_clock]=u;
if(son[u]) dfs2(son[u],topf);
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
}
inline void cover(int o,int l,int r,int v){
tag[o]=v;
nd[o].sum=v*(r-l+1);
if(v>0) nd[o].lmax=nd[o].rmax=nd[o].amax=nd[o].sum;
else nd[o].lmax=nd[o].rmax=nd[o].amax=0;
}
inline void pushdown(int o,int l,int r){
if(tag[o]==-INF) return;
int mid=(l+r)>>1;
cover(lson,tag[o]);cover(rson,tag[o]);
tag[o]=-INF;
}
void build(int o,int l,int r){
tag[o]=-INF;
if(l==r){
nd[o].sum=w[id[l]];
nd[o].lmax=nd[o].rmax=nd[o].amax=max(0,w[id[l]]);
return;
}
int mid=(l+r)>>1;
build(lson);build(rson);
nd[o]=nd[o<<1]+nd[o<<1|1];
}
void update(int o,int l,int r,int ql,int qr,int v){
if(l>=ql && r<=qr) return cover(o,l,r,v);
int mid=(l+r)>>1;
pushdown(o,l,r);
if(mid>=ql) update(lson,ql,qr,v);
if(mid<qr) update(rson,ql,qr,v);
nd[o]=nd[o<<1]+nd[o<<1|1];
}
node query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return nd[o];
int mid=(l+r)>>1;
pushdown(o,l,r);
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
return query(lson,ql,qr)+query(rson,ql,qr);
}
void chain_update(int u,int v,int x){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,1,n,dfn[top[u]],dfn[u],x);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,1,n,dfn[u],dfn[v],x);
}
int chain_query(int u,int v){
node un,vn;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v),swap(un,vn);
un=query(1,1,n,dfn[top[u]],dfn[u])+un;
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v),swap(un,vn);
vn=query(1,1,n,dfn[u],dfn[v])+vn;
swap(un.lmax,un.rmax);
return (un+vn).amax;
}
int main(){
n=read();
FOR(i,1,n) w[i]=read();
FOR(i,1,n-1){
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs1(1,0);dfs2(1,1);build(1,1,n);
m=read();
FOR(i,1,m){
int op=read(),a=read(),b=read();
if(op==2) chain_update(a,b,read());
else printf("%d
",chain_query(a,b));
}
}
GSS7
后面的,留坑待填……
總結
以上是生活随笔為你收集整理的SPOJ GSS系列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#中字符串大小比较函数--Compar
- 下一篇: node,cnpm安装和配置