来自FallDream的博客,未经允许,请勿转载,谢谢。
------------------------------------------------------------------------------------------
我好菜啊都不会,ditoly轻松切掉T1,险些rank1,我死搞T3,到最后log^2都不会,真的菜。
------------------------------------------------------------------------------------------
A.约会
给定一棵n个节点的树,每个点有权值。m次操作1.单点加2.子树加3.求满足$lca(x,y)=z$的$w[x]+w[y]$的期望值。n,m<=300000
题解:对于以点a为根的一棵子树,假设它有孩子x1..xk,size(x)表示子树x的大小,sum(x)表示子树x的权值和,w(x)表示点x的权值,那么点$lca(x,y)=a$的权值总和为:
$(size(a)-size(x1))*sum(x1)+(size(a)-size(x1)*sum(x2)+..+(size(a)-size(k))*sum(k)+w(a)*(size(a)-1)$
方案数同理,可以看作每一个点对的权值和都是1,那么我们把它展开得到所求的期望值是:
$$\frac{2*size(a)*sum(a)-\sum_{i=1}^{k}sum(xk)*size(xk)-w(a)}{size(a)^2-\sum_{i=1}^{k}size(xk)^{2}-1}$$
两部分可以预处理,那么我们考虑处理修改操作对上面部分的影响。
如果改的是一棵子树,那么可以看作所有点对的期望都实际增加了$2*ad$,我们在一棵线段树上区间改。
如果改的是一个节点,那么代入公式我们会发现它实际增加了$2*ad*(size[x]-1)$,直接记下即可。
处理完了修改操作对子树的影响,接下来要考虑对这个操作到跟节点的一条链上的节点的影响。
同样带入公式,得到对于一个点a,修改的点在它的子树b中,对它的影响是$ad*(size(a)-size(b))$
于是我们对这棵树树剖,然后最多只要处理logn条重链,对于重链上的点线段树区间修改,对于重链之间的连接点,直接暴力改它就好啦。
复杂度$nlog^{2}n$
#include<iostream> #include<cstdio> #define MN 300000 #define ll long long #define ld long double using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } ld w1[MN+5]; char op[5]; ll sum[MN+5],w[MN+5],s1[MN+5]; int size[MN+5],mx[MN+5],head[MN+5],cnt=0,dfn=0,n,m,top[MN+5],nl[MN+5],nr[MN+5],fa[MN+6]; struct edge{int to,next;}e[MN+5]; struct TREE{int l,r;ll x,val;}T1[MN*4+5],T2[MN*4+5],T3[MN*4+5]; inline void ins(int f,int t){e[++cnt]=(edge){t,head[f]};head[f]=cnt;} void build(TREE*T,int x,int l,int r) { if((T[x].l=l)==(T[x].r=r))return; int mid=l+r>>1; build(T,x<<1,l,mid);build(T,x<<1|1,mid+1,r); } void pushdown(TREE*T,int x) { int l=x<<1,r=x<<1|1; T[l].x+=T[x].val;T[r].x+=T[x].val; T[l].val+=T[x].val;T[r].val+=T[x].val; T[x].val=0; } void renew(TREE*T,int x,int l,int r,ll ad) { if(T[x].l==l&&r==T[x].r) {T[x].x+=ad;T[x].val+=ad;return;} int mid=T[x].l+T[x].r>>1; if(r<=mid)renew(T,x<<1,l,r,ad); else if(l>mid)renew(T,x<<1|1,l,r,ad); else {renew(T,x<<1,l,mid,ad);renew(T,x<<1|1,mid+1,r,ad);} } ll query(TREE*T,int x,int k) { if(T[x].l==T[x].r)return T[x].x; if(T[x].val!=0)pushdown(T,x); int mid=T[x].l+T[x].r>>1; return k<=mid?query(T,x<<1,k):query(T,x<<1|1,k); } void dfs1(int x) { size[x]=1;mx[x]=0;sum[x]=w[x];s1[x]=-1;w1[x]=-w[x];int maxn=0; for(int i=head[x];i;i=e[i].next) { dfs1(e[i].to);size[x]+=size[e[i].to];sum[x]+=sum[e[i].to]; if(size[e[i].to]>maxn){maxn=size[e[i].to];mx[x]=e[i].to;} w1[x]-=size[e[i].to]*sum[e[i].to];s1[x]-=1LL*size[e[i].to]*size[e[i].to]; } w1[x]+=sum[x]*size[x];s1[x]+=1LL*size[x]*size[x]; } void dfs2(int x,int tp) { nl[x]=++dfn;top[x]=tp;if(mx[x]) dfs2(mx[x],tp); for(int i=head[x];i;i=e[i].next) if(e[i].to!=mx[x])dfs2(e[i].to,e[i].to); nr[x]=dfn; } void up(int x,ll ad) { for(;x!=0;x=fa[top[x]]) { if(x!=top[x]) renew(T2,1,nl[top[x]],nl[x]-1,ad); if(fa[top[x]]) w1[fa[top[x]]]+=ad*(size[fa[top[x]]]-size[top[x]]); } } int main() { n=read();m=read();build(T1,1,1,n);build(T2,1,1,n); for(int i=2;i<=n;i++) ins(fa[i]=read(),i); for(int i=1;i<=n;i++) w[i]=read(); dfs1(1);dfs2(1,1); for(int i=1;i<=m;i++) { scanf("%s",op);int x=read(); if(op[0]=='Q'){printf("%.6f\n",size[x]>1?2*query(T1,1,nl[x])+(double)2*(double)((double)w1[x]+(ld)query(T2,1,nl[x])*(size[x]-size[mx[x]]))/s1[x]:0);} else if(op[0]=='S'){int ad=read();w1[x]+=1LL*(size[x]-1)*ad;up(x,ad);} else {int ad=read();renew(T1,1,nl[x],nr[x],ad);up(x,1LL*(nr[x]-nl[x]+1)*ad);} } return 0; }
3.山脉
给定n座山,每座山都有一个高度,需要支持两个操作:1)给区间[l,r]加上一个首项为a,公差为d(>0)的等差数列 2)询问有多少个点比它两旁的点都大。$n,m\leqslant 10^{5}$
题解:很容易想到改为维护差分数组,这样题目转换为维护一个n-1个点的序列,每次修改两个单点和一段区间,求第一个是正的,第二个是负的的长度为2的区间的个数。
由于区间加的公差d是正的,一开始最多有n-1个负数,每次操作最多加入一个负数,所以我们可以直接线段树维护区间最大的负数,每次找到最大的负数,判断是否会变成正数或者0,然后暴力更新答案,这个的次数是$O(n)$的,所以复杂度$O(nlogn)$
#include<iostream> #include<cstdio> #include<vector> #define MAXN 100000 #define INF 200000000000LL #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m; int s[MAXN+5],ans=0; vector<int> v; struct data { ll x;int from; data operator + (data y) { return ((y.x<=0&&y.x>=x)||x>0)?y:*this; } }; struct TREE{int l,r;ll val;data x;}T[MAXN*4+5]; void build(int x,int l,int r) { if((T[x].l=l)==(T[x].r=r)){T[x].x=(data){s[l],l};return;} int mid=l+r>>1; build(x<<1,l,mid);build(x<<1|1,mid+1,r); T[x].x=T[x<<1].x+T[x<<1|1].x; } void pushdown(int x) { int l=x<<1,r=x<<1|1; T[l].x.x+=T[x].val;T[r].x.x+=T[x].val; T[l].val+=T[x].val;T[r].val+=T[x].val; T[x].val=0; } void modify(int x,int l,int r,ll ad) { if(T[x].l==l&&T[x].r==r) { if(T[x].x.x<=0&&T[x].x.x+ad>=0&&l!=r) { T[x].x.x+=ad;T[x].val+=ad;pushdown(x); int mid=l+r>>1; modify(x<<1,l,mid,ad);modify(x<<1|1,mid+1,r,ad); T[x].x=T[x<<1].x+T[x<<1|1].x; return; } T[x].val+=ad;T[x].x.x+=ad; return; } if(T[x].val)pushdown(x); int mid=T[x].l+T[x].r>>1; if(r<=mid)modify(x<<1,l,r,ad); else if(l>mid)modify(x<<1|1,l,r,ad); else {modify(x<<1,l,mid,ad);modify(x<<1|1,mid+1,r,ad);} T[x].x=T[x<<1].x+T[x<<1|1].x; } data query(int x,int l,int r) { if(T[x].l==l&&r==T[x].r)return T[x].x; if(T[x].val)pushdown(x);int mid=T[x].l+T[x].r>>1; if(r<=mid) return query(x<<1,l,r); else if(l>mid)return query(x<<1|1,l,r); else return query(x<<1,l,mid)+query(x<<1|1,mid+1,r); } void calc(int x,int ad) { if(x!=1)ans-=(query(1,x,x).x<0&&query(1,x-1,x-1).x>0); if(x!=n-1)ans-=(query(1,x,x).x>0&&query(1,x+1,x+1).x<0); if(x!=1)ans+=(query(1,x,x).x+ad<0&&query(1,x-1,x-1).x>0); if(x!=n-1)ans+=(query(1,x,x).x+ad>0&&query(1,x+1,x+1).x<0); } int main() { n=read();m=read(); for(int i=1;i<=n;i++)s[i]=read(); for(int i=1;i<n;i++)s[i]=s[i+1]-s[i]; for(int i=1;++i<n;)ans+=(s[i-1]>0&&s[i]<0); build(1,1,n-1);printf("%d\n",ans); for(int i=1;i<=m;i++) { int l=read(),r=read(),a=read(),d=read(); if(l>1)calc(l-1,a),modify(1,l-1,l-1,a); if(r<n)calc(r,-(a+(r-l)*d)),modify(1,r,r,-(a+d*(r-l))); if(l<r) { data now=query(1,l,r-1); while(l<r&&now.x<=0&&now.x+d>=0) { calc(now.from,d); modify(1,now.from,now.from,INF); v.push_back(now.from); now=query(1,l,r-1); } modify(1,l,r-1,d); for(int j=0;j<v.size();j++) modify(1,v[j],v[j],-INF); v.clear(); } printf("%d\n",ans); } return 0; }