10.6总结
T1
lca板子
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e4+10;
int dp[maxn][21],dep[maxn];
vector<int>g[maxn];
void addline(int u,int v) {
g[u].push_back(v);
g[v].push_back(u);
}
void dfs(int u,int fa){
dp[u][0]=fa;
dep[u]=dep[fa]+1;
for(int i=1;i<=20;i++) {
dp[u][i]=dp[dp[u][i-1]][i-1];
}
for(int i=0;i<g[u].size();i++) {
int v=g[u][i];
if(v!=fa) {
dep[v]=dep[u]+1;
dfs(v,u);
}
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) {
swap(x,y);
}
for(int i=20;i>=0;i--) {
if(dep[dp[x][i]]>=dep[y]) {
x=dp[x][i];
}
}
if(x==y) {
return x;
}
for(int i=20;i>=0;i--) {
if(dp[x][i]!=dp[y][i]) {
x=dp[x][i];
y=dp[y][i];
}
}
return dp[x][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,rt;
cin>>n;
for(int i=1;i<=n;i++) {
int x,y;
cin>>x>>y;
if(y==-1){
rt=x;
} else {
addline(x,y);
}
}
dep[rt]=1;
dfs(rt,0);
int m;
cin>>m;
while(m--) {
int x,y;
cin>>x>>y;
if(x==lca(x,y)) {
cout<<1;
} else if(y==lca(x,y)) {
cout<<2;
} else{
cout<<0;
}
cout<<endl;
}
return 0;
}
T2
首先对于树上三个点汇聚在一个点上会有两种情况
可以看出,三个点要么有共同的祖先要么可以汇聚在三个点中的一个点
然后我们要求到公共点的距离,由于不知道公共点在哪里,所以我们通过公共点走到其他两个点,画记一遍路线可得走的总距离是公共点到其他三点距离的两倍,即公共点到其他点的距离为
然后因为
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+7;
vector<int>g[maxn];
void addline(int u,int v) {
g[u].push_back(v);
g[v].push_back(u);
}
int dep[maxn],dp[maxn][21];
void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
dp[u][0]=fa;
for(int i=1;i<=20;i++) {
dp[u][i]=dp[dp[u][i-1]][i-1];
}
for(int i=0;i<g[u].size();i++) {
int v=g[u][i];
if(v!=fa) {
dep[v]=dep[u]+1;
dfs(v,u);
}
}
}
int lca(int x,int y){
if(dep[x]>dep[y]) {
swap(x,y);
}
for(int i=20;i>=0;i--) {
if(dep[dp[y][i]]>=dep[x]) {
y=dp[y][i];
}
}
if(x==y) {
return x;
}
for(int i=20;i>=0;i--) {
if(dp[x][i]!=dp[y][i]) {
x=dp[x][i];
y=dp[y][i];
}
}
return dp[x][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<n;i++) {
int u,v;
cin>>u>>v;
addline(u,v);
}
dfs(1,0);
for(int i=1;i<=m;i++) {
int x,y,z,ans;
cin>>x>>y>>z;
int l1=lca(x,y),l2=lca(y,z),l3=lca(z,x);
if(l1==l2) {
ans=l3;
} else if(l2==l3) {
ans=l1;
} else if(l1==l3){
ans=l2;
}
cout<<ans<<' '<<dep[x]+dep[y]+dep[z]-dep[l1]-dep[l2]-dep[l3]<<'\n';
}
return 0;
}
T3
首先要翻译题面的行为
懒得敲字了自己看图
将x作为原来y中包含x的子树的根,原来的根变为它的儿子
而我们不难发现,x儿子的深度是不变的而变的只有z和x的深度
首先如果x就是y的直接儿子说明转换如同没转换一样,可以直接输出f[y]
否则我们可以看见,变化的只有x和z的深度,x深度变小而z的深度变大的数值相同,意味着需要-siz[x]+siz[z],x的深度变成了dep[y]+1则意味着点权和边权的乘积增加
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
const int maxn = 5e5+10;
vector<int>g[maxn];
int v[maxn], p[maxn], dep[maxn];
int dp[maxn][22];
int siz[maxn], f[maxn];
void addline(int u,int v) {
g[u].push_back(v);
g[v].push_back(u);
}
void dfs(int u,int fa){
siz[u] = v[u];
dp[u][0] = fa;
dep[u] = dep[fa] + 1;
for(int i=1;i<=20;i++) {
dp[u][i]=dp[dp[u][i-1]][i-1];
}
for(int i=0;i<g[u].size();i++) {
int v=g[u][i];
if(v!=fa) {
dfs(v, u);
siz[u] += siz[v];
f[u] += f[v];
}
}
f[u] += (siz[u] - v[u]);
}
int lca(int x,int y){
for(int i=20;i>=0;i--) {
if(dep[dp[x][i]]>dep[y]) {
x=dp[x][i];
}
}
return x;
}
int dist(int x,int y) {
return dep[x]+dep[y]-2*dep[lca(x,y)];
}
int count(int x,int y) {
return dist(x,y)*v[x];
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) {
cin>>v[i];
}
for(int i=2;i<=n;i++) {
cin>>p[i];
addline(p[i],i);
}
dfs(1,0);
while(q--) {
int x,y;
cin>>x>>y;
if(p[x]==y){
cout<<f[y]<<'\n';
continue;
}
int z=lca(x,y);
cout << f[y] - (dep[x] - dep[y] - 1) * v[x] + (siz[z] - siz[x]) << "\n";
}
return 0;
}