size of a subtree
CF div2 D Maximum Distributed Tree
题意:给一棵n节点树,给一个大数m,所有边权的积要等于m。要求边权为1的边最少,
∑
i
=
1
n
−
1
(
w
i
,
z
i
)
\sum_{i=1}^{n-1}(w_i,z_i)
∑i=1n−1(wi,zi)最大。
w
i
w_i
wi是边权;
z
i
z_i
zi是该边经过次数(当计算distribution index时(见图))
思路:贪心+计算一条边两边各连接了多少节点(借助子树(DFS))。
贪心:
- For four positive integers a,b,c,d (a≥b,c≥d), ac+bd≥ad+bc
- For five positive integers a,b,c,d,e (a≥b,d≥e), acd+be≥bcd+ae
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int mod = 1e9+7;
typedef long long ll;
int sz[maxn],x[maxn],y[maxn];
ll p[maxn],q[maxn];
vector<int>G[maxn];
void dfs(int u){
sz[u] = 1;
for(auto i:G[u]) if(!sz[i]) dfs(i),sz[u] += sz[i];
}
void init(int n){
for(int i=1;i<=n;++i) G[i].clear();
memset(sz,0,sizeof(sz));
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d",&n);
init(n);
for(int i=1;i<n;++i)
scanf("%d%d",&x[i],&y[i]),
G[x[i]].push_back(y[i]),
G[y[i]].push_back(x[i]);
scanf("%d",&m);
for(int i=1;i<=m;++i)
scanf("%d",&p[i]);
sort(p+1,p+1+m);
for(;m>n-1;--m) p[m-1]=p[m]*p[m-1]%mod;
for(;m<n-1;++m) p[m+1] = 1;
if(p[m]==1) sort(p+1,p+1+m);
dfs(1);
for(int i=1;i<n;++i)
q[i] = min(sz[x[i]],sz[y[i]]),
q[i] = 1ll*(n-q[i])*q[i];
sort(q+1,q+n);
ll ans = 0;
for(int i=1;i<n;++i,ans%=mod)
ans += q[i]%mod*p[i]%mod;
printf("%d\n",ans);
}
}
diameter of a tree
用途/性质
- 最远距离
- 两次dfs:第一次任一点为根,找最远;第二次以当前为根,找最远。
题目
CF Tree Destruction
问题:给一棵树,每次选两个叶子,答案加上他们的距离,然后销毁两个叶子中的一个;求最大的答案。
sol:最优方案:找到直径,然后依次销毁直径以外的叶子(第一部分)。最后只剩下直径的时候,销毁直径的叶子(第二部分)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
vector<int>G[maxn];
vector<pair<int,int>>a;
int dep[maxn],f[maxn],in[maxn];
int dia,d;
ll ans;
void dfs(int u,int fa){
dep[u]=dep[f[u]=fa]+1;
if(dep[u]>dia) dia = dep[u],d = u;
for(auto i:G[u]) if(i-fa) dfs(i,u);
}
void dfs2(int u,int fa,int dis,int flg = 0){
in[u] = 1;
for(auto i:G[u]) if(!in[i]) dfs2(i,fa,dis+1,1);
if(flg) ans += dis,a.push_back(make_pair(fa,u));
} //回溯加上第一部分的答案
int main(){
int n,x,y;
scanf("%d",&n);
for(int i=1;i<n;++i) scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
dfs(1,0);
x = d;
dia = 0,dfs(x,0);
y = d;
ans = (ll)(dia-1)*dia/2; //第二部分的答案
for(int i=y;i;i=f[i]) in[i] = 1;
for(int i=y;i;i=f[i])
if(dep[i]-1>dep[y]-dep[i]) dfs2(i,x,dep[i]-1);
else dfs2(i,y,dep[y]-dep[i]);
printf("%lld\n",ans);
for(auto i:a) printf("%d %d %d\n",i.first,i.second,i.second);
for(int i=y;i-x;i=f[i]) printf("%d %d %d\n",x,i,i);
}
LCA
algorithm
- 倍增binary lifting
- Tarjan
- 用欧拉序列转化为 RMQ 问题
- 树链剖分
- 动态树
- 标准RMQ
用途/性质
- 树上距离
题目
- 倍增:处理两个地方:(1)达到同一深度(2)找lca
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=500005;
int n,m,rt;
int d[maxn],f[maxn][22];
int lg[maxn];
vector<int>G[maxn];
void dfs(int u,int fa){
d[u]=d[fa]+1;
f[u][0]=fa;
//f[u][0]是父亲,然后是第二个祖先,第四个祖先,。。。以此类推
for(int i=0;i<lg[d[u]];++i) f[u][i+1]=f[f[u][i]][i];
for(auto i:G[u]) if(i!=fa) dfs(i,u);
}
int lca(int x,int y){
if(d[x]<d[y]) swap(x,y); //x的深度大
//倍增【1】:先跳到同一深度。只要前者深度大于后者,前者跳到第2^【log2(深度差)-1】(即:深度差 - 1)个祖先
while(d[x]>d[y]) x = f[x][lg[d[x]-d[y]]-1];
if(x == y) return x; //此时深度相同。两者相等的话说明原先的y是x的祖先。此时的x或y就是lca
/*
倍增【2】:找lca。
【二进制思维】从大到小尝试往上跳 如果两者祖先不相等就往上跳:
x跳到第2^【log2(x的深度)-1】(即:x的深度-1)个祖先
鉴于x和y的深度一直一样,因此如果祖先相等,说明可能超过了lca的位置。
*/
for(int k = lg[d[x]]-1;k>=0;--k)
if(f[x][k]!=f[y][k]) x = f[x][k],y = f[y][k];
return f[x][0];
}
int main(){
scanf("%d%d%d",&n,&m,&rt);
for(int i=1;i<=n;++i) lg[i] = lg[i-1] + (1<<lg[i-1] == i);
int x,y,k;
for(int i=1;i<n;++i) scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
// 常数优化,求出 log2(x) + 1
dfs(rt,0);
for(int i=1;i<=m;++i) scanf("%d%d",&x,&y),printf("%d\n",lca(x,y));
}
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=40005;
int n,m,rt;
int d[maxn],f[maxn][22];
int lg[maxn];
int l[maxn];
vector<pair<int,int>>G[maxn];
void dfs(int u,int fa){
d[u]=d[fa]+1;
f[u][0]=fa;
for(int i=0;i<lg[d[u]];++i) f[u][i+1]=f[f[u][i]][i];
for(auto i:G[u]) if(i.first!=fa) l[i.first]=l[u]+i.second,dfs(i.first,u);
}
int lca(int x,int y){
if(d[x]<d[y]) swap(x,y);
while(d[x]>d[y]) x = f[x][lg[d[x]-d[y]]-1];
if(x == y) return x;
for(int k = lg[d[x]]-1;k>=0;--k)
if(f[x][k]!=f[y][k]) x = f[x][k],y = f[y][k];
return f[x][0];
}
void init(int n){
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
for(int i=1;i<=n;++i) G[i].clear();
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) lg[i] = lg[i-1] + (1<<lg[i-1] == i);
init(n);
int x,y,k;
for(int i=1;i<n;++i) scanf("%d%d%d",&x,&y,&k),G[x].push_back(make_pair(y,k)),G[y].push_back(make_pair(x,k));
dfs(1,0);
for(int i=1;i<=m;++i) scanf("%d%d",&x,&y),printf("%d\n",l[x]+l[y]-2*l[lca(x,y)]);
}
}
树形DP
基于DFS
换根DP
原理
父节点与直系儿子节点之间存在一个递推关系。
在dfs过程中进行递推。
例题
此处,dp[i]=dp[u]-pt[i]+(n-pt[i])
意为,以i为根的树到达所有节点的距离和是他的父节点到达所有结点的距离和减掉i的子树大小*1(距离都缩短了1),又对于其他节点距离延长了1.
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define V std::vector<int>
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
#define inf 0x3f3f3f3f
const int N=1e6+5;
V G[N];int n;
int dp[N],pt[N],dis[N];
void dfs(int u,int fa){
for(auto i:G[u]) if(i-fa) dfs(i,u),dis[u]+=dis[i]+pt[i],pt[u]+=pt[i];
pt[u]+=1;
}
void DP(int u,int fa){
for(auto i:G[u]) if(i-fa) dp[i]=dp[u]-pt[i]+(n-pt[i]),DP(i,u);
}
int main(){
int u,v;scanf("%d",&n);
FOR(i,1,n-1) scanf("%d%d",&u,&v),G[u].pb(v),G[v].pb(u);
dfs(1,-1);
dp[1]=dis[1];
DP(1,-1);
int ans=inf;
FOR(i,1,n) ans=min(ans,dp[i]);
printf("%d\n",ans);
}
点分治
//
// Created by Artist on 2021/9/11.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
void io() {ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);}
const int maxn = 1e4+4;
const int maxm = 1e2+4;
vector<pair<int,int> > G[maxn];
int rt;
int que[maxm],m;
int res[maxm];
/*
*
6 5
1 2 1
1 3 2
1 4 3
3 5 4
3 6 2
1
3
6
10
7
*/
int maxsz[maxn],siz[maxn],mnmxsz,vis[maxn]; // 每个点的最大子树大小
void getroot(int u,int fa,int sz){
siz[u]=1;
maxsz[u] = 0;
for(auto o:G[u]){
int v=o.fi;
if(v==fa||vis[v]) continue;
getroot(v,u,sz);
maxsz[u] = max(siz[v],maxsz[u]);
siz[u] += siz[v];
}
maxsz[u] = max(sz-siz[u],maxsz[u]);
if(maxsz[u]<mnmxsz) rt = u,mnmxsz = maxsz[u];
}
int nod[maxn],val[maxn],tot,bel[maxn];
void getdis(int u,int pa,int fa,int dis){
nod[++tot] = u;
bel[u] = pa;
val[u] = dis;
for(auto o:G[u]){
int v=o.fi;
if(vis[v]||v==fa) continue;
getdis(v,pa,u,dis+o.se);
}
}
bool cmp(int x,int y){
return val[x]<val[y];
}
void calc(int u){
tot = 0;
nod[++tot] = u;
val[u] = 0;
bel[u] = u; // 属于哪棵子树
for(auto o:G[u]){
int v=o.fi;
if(vis[v]) continue;
getdis(v,v,u,o.se);
}
sort(nod+1,nod+tot+1,cmp);
for(int i=1;i<=m;++i){
int l=1,r=tot;
while(l<r){
if(val[nod[l]]+val[nod[r]]>que[i]) r--;
else if(val[nod[l]]+val[nod[r]]<que[i]) l++;
else if(bel[nod[l]]==bel[nod[r]]) {
if(val[nod[r]]==val[nod[r-1]]) r--;
else l++;
}
else{
res[i] = 1;
break;
}
}
}
}
void solve(int u){
vis[u] = 1;
calc(u);
for(auto o:G[u]){
int v=o.fi;
if(vis[v]) continue;
mnmxsz = maxn+3;
getroot(v,0,siz[v]);
solve(rt);
}
}
signed main() {
io();int n;cin>>n>>m;
for(int i=1;i<n;++i){
int u,v,w;cin>>u>>v>>w;
G[u].pb(mkp(v,w));
G[v].pb(mkp(u,w));
}
for(int i=1;i<=m;++i){
cin>>que[i];
if(!que[i]) res[i]=1;
}
mnmxsz = maxn+3;
getroot(1,0,n);
solve(rt); //
for(int i=1;i<=m;++i){
cout<<(res[i]?"AYE":"NAY")<<endl;
}
}
点分治2 tree
//
// Created by artist on 2021/9/12.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
#define pii pair<int,int>
#define int long long
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 4e4+4;
vector<pair<int,int> > G[maxn];
int rt,mxsz[maxn],sz[maxn],vis[maxn],K;
ll ans;
void getrt(int u,int fa,int siz){
sz[u]=1;
mxsz[u]=0;
for(auto o:G[u]){
int v=o.fi;
if(v==fa||vis[v]) continue;
getrt(v,u,siz);
sz[u] += sz[v];
mxsz[u] = max(mxsz[u],sz[v]);
}
mxsz[u] = max(mxsz[u],siz-sz[u]);
if(mxsz[u]<mxsz[rt]) rt=u;
}
int nod[maxn],tot;
void getdis(int u,int fa,int dis){
nod[++tot] = dis;
for(auto o:G[u]){
int v=o.fi;
if(vis[v]||v==fa) continue;
getdis(v,u,dis+o.se);
}
}
ll calc(int u,int dis){
tot = 0;
getdis(u,0,dis);
sort(nod+1,nod+1+tot);
int l=1,r=tot;
ll res = 0;
while(l<=r){
if(nod[l]+nod[r]<=K) res += r-l,l++;
else r--;
}
return res;
}
void solve(int u){
vis[u]=1;
ans += calc(u,0);
for(auto o:G[u]){
int v=o.fi;
if(vis[v]) continue;
ans -= calc(v,o.se);
rt = 0;
getrt(v,0,sz[v]);
solve(rt);
}
}
signed main() {
io();int n;cin>>n;
for(int i=1;i<n;++i){
int u,v,w;cin>>u>>v>>w;
G[u].pb(mkp(v,w));
G[v].pb(mkp(u,w));
}
cin>>K;
mxsz[0] = maxn;
getrt(1,0,n);
solve(rt);
cout<<ans<<endl;
}
树上差分
//
// Created by artist on 2021/9/22.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
#define pii pair<int,int>
void dbg() { std::cout << " #\n"; }
template<typename T, typename...Args>
void dbg(T a, Args...args) {
std::cout << a << ' ';
dbg(args...);
}
void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 6e4;
const int LOG = __lg(maxn)+1;
int d[maxn];
vector<int> G[maxn];
int dep[maxn],fa[maxn][LOG+3];
int ans;
void init(int u,int f) {
fa[u][0] = f;
for(auto v:G[u]) {
if(v==f) continue;
dep[v] = dep[u] + 1;
init(v,u);
}
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=LOG;~i;--i)
if(dep[fa[x][i]]>=dep[y] && fa[x][i]) x=fa[x][i];
if(x==y) return x;
for(int i=LOG;~i;--i)
if(fa[x][i]!=fa[y][i] && fa[x][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int dfs(int u,int f) {
int cur = d[u];
for(auto v:G[u]) {
if(v==f) continue;
cur += dfs(v,u);
}
ans = max(ans,cur);
return cur;
}
signed main() {
io();
int n,k;cin>>n>>k;
for(int i=1;i<n;++i) {
int x,y;cin>>x>>y;
G[x].pb(y);
G[y].pb(x);
}
init(1,0);
for(int j=1;j<=LOG;++j) for(int i=1;i<=n;++i) fa[i][j] = fa[fa[i][j-1]][j-1];
for(int i=1;i<=k;++i) {
int x,y;cin>>x>>y;
int LCA = lca(x,y);
++d[x],++d[y];
--d[LCA],--d[fa[LCA][0]];
}
dfs(1,0);
cout<<ans<<endl;
}