[Luogu-P3676]小清新資料結構題-題解
【題目地址】
本來應該用動態點分治來做的,複雜度是,但是發現樹鏈剖分好像挺好寫的XD,於是過了…
題意可以去看原題面。
其實我們資料中詢問以1號點為根的,從這裡入手,我們先將每個點的子樹值的和求出來,記為,然後用樹剖把它變成序列,然後再記,其中為子樹中的點,記,我們樹剖後用線段樹維護這兩個值。
考慮序列上維護權值和和和的平方(讀起來有點怪XD)。
首先,以一號點為根的答案就是:
考慮修改操作:
修改一個點的權值,我們考慮其變化量,假設為,那麼會影響到的就是從這條路上的所有點的答案,首先加上變化量,答案就變成了:
所以每次增加的就是和,用個標記,區間修改即可。
現在考慮根不是的情況:
首先記原來根為的答案為,然後我們考慮,當根變成時,會發生變化的只有這條路上的點的貢獻,所以我們記原來每個點的為,換根後的為,那麼新的答案就為:
然後我們看對於改變後的不好求,而開始已經預處理出來了,所以我們考慮和的關係:
可以發現在這條路上,假如一個點的下一個點(從1到r的下一個點)為,那麼就有:
所以我們令,重新編號的話就是,帶回原式,假如有個點:
而又等於,所以帶入式子,每次算一下就好啦,複雜度
用上面線段樹維護的兩個表示,也就是:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=4e5+10;
int n,m;
int val[M];
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=ss(b,head[a]);head[a]=cnt;
g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
int dep[M],f[M],son[M],sze[M],top[M],num[M],rf[M],tim;
ll sum1[M];
ll Sqr(ll a){return a*a;}
void dfs1(int a){
sze[a]=1;sum1[a]=val[a];
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
dep[g[i].to]=dep[a]+1;
f[g[i].to]=a;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
sum1[a]+=sum1[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to])
son[a]=g[i].to;
}
}
void dfs2(int a,int b){
top[a]=b;rf[num[a]=++tim]=a;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==son[a]||g[i].to==f[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
ll S1[M<<2],S2[M<<2],lazy[M<<2],All;
void pushup(int o){
S2[o]=S2[o<<1]+S2[o<<1|1];
S1[o]=S1[o<<1]+S1[o<<1|1];
}
void pushdown(int o,int l,int r,int mid){
if(!lazy[o]) return;
lazy[o<<1]+=lazy[o];
lazy[o<<1|1]+=lazy[o];
S2[o<<1]+=Sqr(lazy[o])*(mid-l+1)+2ll*lazy[o]*S1[o<<1];
S2[o<<1|1]+=Sqr(lazy[o])*(r-mid)+2ll*lazy[o]*S1[o<<1|1];
S1[o<<1]+=(mid-l+1)*lazy[o];
S1[o<<1|1]+=(r-mid)*lazy[o];
lazy[o]=0;
}
void build(int o,int l,int r){
if(l==r){
S1[o]=sum1[rf[l]];
S2[o]=Sqr(S1[o]);
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void update(int o,int l,int r,int L,int R,ll v){
if(L<=l&&r<=R){
S2[o]+=Sqr(v)*(r-l+1)+2ll*v*S1[o];
S1[o]+=(r-l+1)*v;
lazy[o]+=v;
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) update(o<<1,l,mid,L,R,v);
if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
pushup(o);
}
void query(int o,int l,int r,int L,int R,ll &a1,ll &a2){
if(L<=l&&r<=R){
a1+=S1[o];a2+=S2[o];
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) query(o<<1,l,mid,L,R,a1,a2);
if(R>mid) query(o<<1|1,mid+1,r,L,R,a1,a2);
}
ll Query(int a){
ll ans=0,s1=0,ls=0,k=dep[a];
query(1,1,n,1,n,ls,ans);
while(top[a]!=top[1]){
query(1,1,n,num[top[a]],num[a],s1,ls);
a=f[top[a]];
}
query(1,1,n,num[1]+1,num[a],s1,ls);
return ans+All*(k*All-2ll*s1);
}
void Update(int a,ll b){
b-=val[a];All+=b;val[a]+=b;
while(a){
update(1,1,n,num[top[a]],num[a],b);
a=f[top[a]];
}
}
int a,b,opt;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
dfs1(1);
dfs2(1,1);
build(1,1,n);
All=sum1[1];
while(m--){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&a,&b);
Update(a,b);
}else{
scanf("%d",&a);
printf("%lld\n",Query(a));
}
}
return 0;
}
- 動態點分治
維護原理差不多,放到點分樹上而已。
詳細講解先咕咕咕.
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=2e5+10;
int n,m;
ll vv[M];
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=ss(b,head[a]);head[a]=cnt;
g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
namespace LCA{
int dep[M],top[M],f[M],son[M],sze[M];
void dfs1(int a){
sze[a]=1;
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
f[g[i].to]=a;
dep[g[i].to]=dep[a]+1;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to]){
son[a]=g[i].to;
}
}
}
void dfs2(int a,int b){
top[a]=b;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==son[a]||g[i].to==f[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
return a;
}
int getdep(int a,int b){
return dep[a]+dep[b]-2*dep[lca(a,b)];
}
void init(){
dfs1(1);
dfs2(1,1);
}
}
int root,sum;
int sze[M],son[M],f[M];
bool vis[M];
void getroot(int a,int fa){
son[a]=0;sze[a]=1;
for(int i=head[a];i;i=g[i].last){
if(vis[g[i].to]||g[i].to==fa) continue;
getroot(g[i].to,a);
sze[a]+=sze[g[i].to];
son[a]=max(son[a],sze[g[i].to]);
}
son[a]=max(son[a],sum-sze[a]);
if(son[a]<son[root])root=a;
}
void solve(int a,int b){
f[a]=b;vis[a]=1;
for(int i=head[a];i;i=g[i].last){
if(vis[g[i].to]) continue;
sum=sze[g[i].to];root=0;
getroot(g[i].to,0);
solve(root,a);
}
}
ll val[M],s1[M],s2[M],s[M],All,Now;
void update(int a,ll b){
val[a]+=b;
for(int i=a;f[i];i=f[i]){
int v=LCA::getdep(a,f[i]);
val[f[i]]+=b;
s1[f[i]]+=b*v;
s2[i]+=b*v;
}
}
ll query(int a){
ll now=s1[a];
for(int i=a;f[i];i=f[i]){
int v=LCA::getdep(a,f[i]);
now+=1ll*v*(val[f[i]]-val[i]);
now+=s1[f[i]]-s2[i];
}
return now;
}
void dfs(int a,int b){
s[a]=vv[a];
for(int i=head[a];i;i=g[i].last){
if(g[i].to==b) continue;
dfs(g[i].to,a);
s[a]+=s[g[i].to];
}
Now+=s[a]*(All-s[a]);
}
void Update(int a,ll b){
b-=vv[a];
update(a,b);
All+=b;vv[a]+=b;
Now+=b*query(a);
}
ll Query(int a){return (query(a)+All)*All-Now;}
int a,b,opt;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++){scanf("%lld",&vv[i]);All+=vv[i];}
dfs(1,0);
LCA::init();
root=0;son[0]=M;
getroot(1,0);
solve(root,0);
for(int i=1;i<=n;i++)update(i,vv[i]);
while(m--){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&a,&b);
Update(a,b);
}else{
scanf("%d",&a);
printf("%lld\n",Query(a));
}
}
return 0;
}
相關文章
- 資料結構測試題。小問題見大智慧!資料結構
- leetcode演算法資料結構題解---資料結構LeetCode演算法資料結構
- 部落格園主題分享-仿FANFOU-小清新主題
- 【整理】資料結構——題目資料結構
- 資料結構簡單題資料結構
- 資料 結構客觀題複習題集
- 資料結構面試100題資料結構面試
- 資料結構演算法題資料結構演算法
- 資料結構專題練習資料結構
- [做題筆記] 資料結構筆記資料結構
- 資料結構連結串列筆試題資料結構筆試
- [uoj228]基礎資料結構練習題 解題報告資料結構
- iOS小記--使用結構體處理資料表的問題iOS結構體
- 解決AI的小資料問題AI
- 資料結構專題頁(更新中...)資料結構
- 03.Java資料結構問題Java資料結構
- 資料結構括號匹配問題資料結構
- 【資料結構】停車場問題資料結構
- 好題——數學與資料結構資料結構
- 資料結構:線性表-例題資料結構
- 資料結構——RMQ(ST表)問題資料結構MQ
- Java刷題常用的資料結構總結Java資料結構
- 資料結構連結串列各種問題資料結構
- 資料結構和演算法面試題系列—數字題總結資料結構演算法面試題
- 用Python解決資料結構與演算法問題(三):線性資料結構之棧Python資料結構演算法
- [JLU] 資料結構與演算法上機題解思路分享-資料結構演算法
- 資料結構和演算法面試題系列—揹包問題總結資料結構演算法面試題
- 資料結構::一些經典的大資料題資料結構大資料
- 資料結構與演算法入門題資料結構演算法
- 資料結構——迴圈佇列PTA習題資料結構佇列
- 【dawn·資料結構】迷宮問題(C++)資料結構C++
- 資料結構初階--堆排序+TOPK問題資料結構排序TopK
- 演算法、資料結構 常見面試題演算法資料結構面試題
- 做題小結
- 實戰資料結構(4)_迴圈單連結串列解決約瑟夫問題資料結構
- FHQ Treap小結(神級資料結構!)資料結構
- 資料結構和演算法面試題系列—連結串列資料結構演算法面試題
- 資料結構基礎和演算法題系列總結資料結構演算法