給出一棵n個點的樹,每個點有兩個權值v,t
有q個操作,有兩種操作:
1.將x到根上的路徑上的點的v值都加上d
2.將x到根上的路徑上的點的t值都加上每個點的v值*d
最後求出所有點的t值
顯然可以直接樹鏈剖分做,不過lazy標記下放真麻煩,因為操作有互相影響
發現一種神標記方法——用矩陣
對於乙個點,它的資訊表示為$$ \begin 1 & 0 & 0 \\ 0 & 1 & 0 \\ v & t & 1 \\ \end $$
而對於第一種操作,就將點的資訊乘上$$ \begin 1 & 0 & 0 \\ 0 & 1 & 0 \\ d & 0 & 1 \\ \end $$
而第二種操作則乘上$$ \begin 1 & d & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end $$
這樣就不用擔心互相影響了,因為矩陣乘法滿足結合律,所以直接將操作按順序乘起來就行了
聽說可以離線cdq做
#include#include#include
#include
#include
using
namespace
std;
typedef
long
long
ll;struct
trnode
tr[210000];int
trlen;
struct
matrix
}d[210000
],cmp;
matrix multi(matrix a,matrix b)}}
returnc;}
void bt(int l,intr)}
struct
node
a[110000];int len,last[110000
];void ins(int x,int y);last[x]=len;}
int son[110000],tot[110000
];void dfs1(intx)}
int top[110000],ys[110000
],z;
void dfs2(int x,int
tp)}
void update(int
now)
void change(int now,int l,int
r)
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2
; update(now);
if(r<=mid) change(lc,l,r);
else
if(l>mid) change(rc,l,r);
else change(lc,l,mid),change(rc,mid+1
,r);
}int fa[110000
];void solve(intx)}
ll d[
110000
];void
out(int
now)
int lc=tr[now].lc,rc=tr[now].rc;
update(now);
if(lc!=-1) out
(lc);
if(rc!=-1) out
(rc);
}int
main()
dfs1(1);
z=0;dfs2(1,1
); trlen=0;bt(1
,z);
cmp.a[
1][1]=cmp.a[2][2]=cmp.a[3][3]=1
;
intq;
scanf("%d
",&q);
while(q--)
out(1
);
for(int i=1;i<=n;i++) printf("
%lld\n
",d[ys[i]]);
return0;
}
51nod1462 樹據結構
給一顆以1為根的樹。每個點有兩個權值 vi,ti,一開始全部是零。q次操作 讀入o,u,d o 1 對u到根上所有點的vi d o 2 對u到根上所有點的ti vi d 最後,輸出每個點的ti值 n,q 100000 有50 的資料n,q 10000 注 所有數64位整數不會爆。我們考慮用樹剖來做這...
51nod 1737 樹的重心
思路 樹的重心也叫樹的質心。找到乙個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,生成的多棵樹盡可能平衡。考慮每一條邊被統計進答案幾次,若斷開這條邊後樹形成大小為s1 s2的兩個聯通塊則這條邊最多被統計min s1,s2 次。刪去重心後任意同一聯通塊中的兩點不構成路...
51 nod 蘋果曼和樹
1500 蘋果曼和樹 基準時間限制 1 秒 空間限制 131072 kb 分值 80 難度 5 級演算法題 收藏 關注 蘋果曼有一棵 n個點的樹。有一些 至少乙個 結點被標記為黑色,有一些結點被標記為白色。現在考慮乙個包含 k 0 k n 條樹邊的集合。如果蘋果曼刪除這些邊,那麼會將這個樹分成 k ...