題目描述

輸入
輸出
樣例輸入
4 5
1 3 2 5
1 2
1 3
2 4
4 2 4
1 2 4
2 3 4
3 1 4 1
4 1 4
樣例輸出
16/3
6/1
提示
對于所有資料滿足 1<=N<=50,000 1<=M<=50,000 1<=Ai<=10^6 1<=D<=100 1<=U,V<=N
前三個操作都很簡單了,LCT就能維護,重點是第四個操作。
求一個區間所有子區間的區間和之和,直接求所有區間和不好求,我們換一種角度去做。
考慮每個點對區間的貢獻,假設目前區間是[l,r],對于區間中的點k(l<=k<=r),它的貢獻就是它的點權*(k-l+1)*(r-k+1)。
那麼我們維護區間答案,考慮怎麼上傳及修改?
先說上傳,就是将一個點的左兒子區間+這個點+這個點的右兒子區間合并。
我們設size[x]為x子樹大小,也就是x子樹所代表的區間的長度;ls代表左子樹,rs代表右子樹。
對于左區間,每個點的貢獻要加上它從左往右數的排名*它的點權*(1+size[rs])。
對于右區間,每個點的貢獻要加上它從右往左數的排名*它的點權*(1+size[ls])。
對于點x要加上它的點權*(1+size[ls])*(1+size[rs])。
發現排名*點權的和無法直接求,是以還要維護兩個資訊lv[x],rv[x],分别代表x子樹所代表區間中每個點點權*從左/從右排名的和。
再看看這兩個資訊怎麼合并,就以lv[x]為例吧,先将左右子節點的lv加上,左子樹lv不變,右子樹的lv發現每個點排名都加了(1+size[ls]),隻要再加上右子樹權值和*(1+size[ls])就好了。
綜上所述,我們需要維護六個變量val,sum,lv,rv,size,ans,分别代表單點權值、子樹權值和、點權*從左數排名之和、點權*從右數排名之和、子樹節點數、區間所有子區間和之和即答案。
再說怎麼修改?設n代表區間長,v為修改時的增量
val和sum比較正常在這就不說了。
lv和rv都加了
而ans則加了
最後這個推一個通項公式就好了。
知道怎麼上傳和修改後剩下的就LCT基本操作了。
但要注意翻轉時也要把lv和rv交換且旋到原樹根的操作splay之後不能隻打标記,要先把目前點左右子樹翻轉,否則目前點的lv和rv是反的。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define ls s[rt][0]
#define rs s[rt][1]
using namespace std;
int n,m;
int x,y;
int opt;
ll z;
int r[50010];
int s[50010][2];
int f[50010];
int st[50010];
ll lv[50010];
ll rv[50010];
ll sum[50010];
ll size[50010];
ll val[50010];
ll ans[50010];
ll a[50010];
ll p,q;
ll res;
int get(int rt)
{
return s[f[rt]][1]==rt;
}
int is_root(int rt)
{
return s[f[rt]][1]!=rt&&s[f[rt]][0]!=rt;
}
void add(int rt,ll v)
{
val[rt]+=v;
sum[rt]+=v*size[rt];
lv[rt]+=v*size[rt]*(size[rt]+1)/2;
rv[rt]+=v*size[rt]*(size[rt]+1)/2;
ans[rt]+=v*size[rt]*(size[rt]+1)*(size[rt]+2)/6;
a[rt]+=v;
}
void flip(int rt)
{
swap(ls,rs);
swap(lv[rt],rv[rt]);
r[rt]^=1;
}
void pushup(int rt)
{
size[rt]=size[ls]+size[rs]+1;
sum[rt]=sum[ls]+sum[rs]+val[rt];
lv[rt]=lv[ls]+lv[rs]+(val[rt]+sum[rs])*(size[ls]+1);
rv[rt]=rv[ls]+rv[rs]+(val[rt]+sum[ls])*(size[rs]+1);
ans[rt]=ans[ls]+ans[rs]+val[rt]*(size[ls]+1)*(size[rs]+1)+lv[ls]*(size[rs]+1)+rv[rs]*(size[ls]+1);
}
void pushdown(int rt)
{
if(r[rt])
{
r[rt]^=1;
flip(ls);
flip(rs);
}
if(a[rt])
{
add(ls,a[rt]);
add(rs,a[rt]);
a[rt]=0;
}
}
void rotate(int rt)
{
int fa=f[rt];
int anc=f[fa];
int k=get(rt);
if(!is_root(fa))
{
s[anc][get(fa)]=rt;
}
s[fa][k]=s[rt][k^1];
f[s[fa][k]]=fa;
s[rt][k^1]=fa;
f[fa]=rt;
f[rt]=anc;
pushup(fa);
pushup(rt);
}
void splay(int rt)
{
int top=0;
st[++top]=rt;
for(int i=rt;!is_root(i);i=f[i])
{
st[++top]=f[i];
}
for(int i=top;i>=1;i--)
{
pushdown(st[i]);
}
for(int fa;!is_root(rt);rotate(rt))
{
if(!is_root(fa=f[rt]))
{
rotate(get(fa)==get(rt)?fa:rt);
}
}
}
void access(int rt)
{
for(int x=0;rt;x=rt,rt=f[rt])
{
splay(rt);
s[rt][1]=x;
pushup(rt);
}
}
void reverse(int rt)
{
access(rt);
splay(rt);
flip(rt);
}
void link(int x,int y)
{
reverse(x);
f[x]=y;
}
void cut(int x,int y)
{
reverse(x);
access(y);
splay(y);
if(s[x][1]||f[x]!=y)
{
return ;
}
s[y][0]=f[x]=0;
pushup(y);
}
void change(int x,int y,ll z)
{
reverse(x);
access(y);
splay(y);
add(y,z);
}
int find(int rt)
{
while(f[rt])
{
rt=f[rt];
}
return rt;
}
void split(int x,int y)
{
reverse(x);
access(y);
splay(y);
}
ll gcd(ll x,ll y)
{
if(y==0)
{
return x;
}
return gcd(y,x%y);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&val[i]);
pushup(i);
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
link(x,y);
}
while(m--)
{
scanf("%d%d%d",&opt,&x,&y);
if(opt==1)
{
if(find(x)==find(y))
{
cut(x,y);
}
}
else if(opt==2)
{
if(find(x)!=find(y))
{
link(x,y);
}
}
else if(opt==3)
{
scanf("%lld",&z);
if(find(x)==find(y))
{
split(x,y);
add(y,z);
}
}
else
{
if(find(x)==find(y))
{
split(x,y);
p=ans[y];
q=size[y]*(size[y]+1)/2;
res=gcd(p,q);
printf("%lld/%lld\n",p/res,q/res);
}
else
{
printf("-1\n");
}
}
}
}
轉載于:https://www.cnblogs.com/Khada-Jhin/p/9748300.html