首页 > 其他分享 >洛谷2664树上游戏-点分治

洛谷2664树上游戏-点分治

时间:2024-04-29 22:23:32浏览次数:28  
标签:sz cnt 洛谷 int sum 分治 2664 path col

link:https://www.luogu.com.cn/problem/P2664
lrb 有一棵树,树的每个节点有个颜色。给一个长度为 \(n\) 的颜色序列,定义 \(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量。以及

\[sum_i=\sum_{j=1}^n s(i, j) \]

现在他想让你求出所有的 \(sum_i\)。


一个暴力的想法:因为是求和,所以可以拆开算贡献。枚举每个颜色 \(c\),将颜色 \(c\) 的点拿出来,会把原树划分成若干个连通块,对每个点的贡献即为 \(n-sz_i\) ,其中 \(sz_i\) 表示 \(i\) 所属连通块大小。连通块用并查集维护,这样是 \(O(n^2)\)的。

考虑点分治,对于当前的分治中心 \(x\),需要考虑:

  • 以 \(x\) 为某个端点,延伸到某个子节点的产生的答案。
  • 以 \(x\) 为LCA,从某个子树中的 \(y\) 出发(或直接从 \(x\) 出发)对另一子树中 \(z\) 的贡献。
    第一种情况直接一次dfs统计:
void dfs1(int x,int fa,int from){
    cnt_col[c[x]]++;
    if(cnt_col[c[x]]==1)ans[from]+=sz[x];
    for(auto to:G[x])if(to!=fa&&!vis[to])dfs1(to,x,from);
    cnt_col[c[x]]--;
}

对第二种情况,假设某种颜色 \(c\) 在 \(x\to y\) 的路径上已经出现过了,统计 \(y\) 的答案时,贡献直接是 \(sz[x]-sz[p]\),否则,应该对 \(p\) 以外的 \(x\) 子树进行统计,如果某个颜色在一个结点里第一次出现,则会产生其子树大小的贡献,记一个 \(path[c]\) 表示颜色 \(c\) 的贡献

void dfs2(int x,int fa,int def_val,bool tag){
    cnt_col[c[x]]++;
    if(cnt_col[c[x]]==1){
        sum_col+=def_val;
        sum_path-=path[c[x]];
    }
    ans[x]+=sum_path;
    if(tag)ans[x]+=sum_col;
    for(auto to:G[x])if(to!=fa&&!vis[to])
        dfs2(to,x,def_val,tag);

    if(cnt_col[c[x]]==1){
        sum_col-=def_val;
        sum_path+=path[c[x]];
    }
    cnt_col[c[x]]--;
}

![[asset/Pasted image 20240429220518.png]]

因为需要扣除掉 \(p\) 子树内的 \(path\) 数组,直接给数组做差不方便,这部分答案可以考虑对 \(x\) 的孩子正反做两次dfs


void calc_path(int x,int fa){
    cnt_col[c[x]]++;
    Q[++tot]=c[x];
    if(cnt_col[c[x]]==1){
        path[c[x]]+=sz[x];
        sum_path+=sz[x];
    }
    for(auto to:G[x])if(to!=fa&&!vis[to])calc_path(to,x);
    cnt_col[c[x]]--;
}
//...
auto work=[&](bool tag){
	clear();
	sum_col=sum_path=0;
	assert(cnt_col[c[x]]==0);
	for(auto to:G[x])if(!vis[to]){
		cnt_col[c[x]]++;
		sum_col=sz[x]-sz[to];
		dfs2(to,x,sz[x]-sz[to],tag);
		calc_path(to,x);
		sum_col=0;
		cnt_col[c[x]]--;
	}
};
work(0);
reverse(G[x].begin(),G[x].end());
work(1);

代码:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int N=1e5+5;
int n,c[N],mx_c;
bool vis[N];
vector<vector<int>> G;

int cnt_col[N],path[N];
int tot,Q[N];

ll sum_path,sum_col;
ll ans[N];
int rt,sz[N],mx_rt;
void get_rt(int x,int fa,int sum){
    sz[x]=1;
    int mx=0;
    for(auto to:G[x])if(to!=fa&&!vis[to]){
        get_rt(to,x,sum);
        mx=max(mx,sz[to]);
        sz[x]+=sz[to];
    }
    mx=max(mx,sum-sz[x]);
    if(mx<mx_rt){
        mx_rt=mx;
        rt=x;
    }
}

void dfs1(int x,int fa,int from){
    cnt_col[c[x]]++;
    if(cnt_col[c[x]]==1)ans[from]+=sz[x];
    for(auto to:G[x])if(to!=fa&&!vis[to])dfs1(to,x,from);
    cnt_col[c[x]]--;
}
void dfs2(int x,int fa,int def_val,bool tag){
    cnt_col[c[x]]++;
    if(cnt_col[c[x]]==1){
        sum_col+=def_val;
        sum_path-=path[c[x]];
    }
    ans[x]+=sum_path;
    if(tag)ans[x]+=sum_col;
    for(auto to:G[x])if(to!=fa&&!vis[to])
        dfs2(to,x,def_val,tag);

    if(cnt_col[c[x]]==1){
        sum_col-=def_val;
        sum_path+=path[c[x]];
    }
    cnt_col[c[x]]--;
}
void calc_path(int x,int fa){
    cnt_col[c[x]]++;
    Q[++tot]=c[x];
    if(cnt_col[c[x]]==1){
        path[c[x]]+=sz[x];
        sum_path+=sz[x];
    }
    for(auto to:G[x])if(to!=fa&&!vis[to])calc_path(to,x);
    cnt_col[c[x]]--;
}
void dfz(int x,int sum=n){
    mx_rt=INF;
    get_rt(x,-1,sum);
    x=rt;
    get_rt(x,-1,sum);
    
    auto clear=[&](){
        rep(i,1,tot)path[Q[i]]=0;
        tot=0;
    };
    dfs1(x,-1,x);
    auto work=[&](bool tag){
        clear();
        sum_col=sum_path=0;
        assert(cnt_col[c[x]]==0);
        for(auto to:G[x])if(!vis[to]){
            cnt_col[c[x]]++;
            sum_col=sz[x]-sz[to];
            dfs2(to,x,sz[x]-sz[to],tag);
            calc_path(to,x);
            sum_col=0;
            cnt_col[c[x]]--;
        }
    };
    work(0);
    reverse(G[x].begin(),G[x].end());
    work(1);

    vis[x]=true;
    for(auto to:G[x])if(!vis[to])dfz(to,sz[to]);
}
int main(){
    fastio;
    cin>>n;
    rep(i,1,n){
        cin>>c[i];
        mx_c=max(mx_c,c[i]);
    }
    G=vector<vector<int>>(n+1);
    rep(i,1,n-1){
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfz(1);
    rep(i,1,n)cout<<ans[i]<<endl;
    return 0;
}

标签:sz,cnt,洛谷,int,sum,分治,2664,path,col
From: https://www.cnblogs.com/yoshinow2001/p/18166745

相关文章

  • 洛谷题单指南-动态规划2-P1091 [NOIP2004 提高组] 合唱队形
    原题链接:https://www.luogu.com.cn/problem/P1091题意解读:要挑选一个最长的先上升后下降的序列,求其余的元素数量解题思路:先计算正向的最长上升子序列,设f[i]表示以i结尾的正向最长上升子序列再计算逆向的最长上升子序列,设g[i]表示以i结尾的逆向最长上升子序列再枚举所有的i<j,m......
  • 洛谷 P5293 [HNOI2019] 白兔之舞
    洛谷传送门所求即为:\[\begin{aligned}f_t&=\sum\limits_{m=0}^L\binom{L}{m}A^m[k\midm-t]\\&=\frac{1}{k}\sum\limits_{m=0}^L\binom{L}{m}A^m\sum\limits_{i=0}^{k-1}\omega_k^{i(m-t)}\\&=\frac{1}{k}\sum\l......
  • 洛谷题单指南-动态规划2-P1004 [NOIP2000 提高组] 方格取数
    原题链接:https://www.luogu.com.cn/problem/P1004题意解读:从起点走到终点,走两次,计算最大路径和,第一次走过的点数值变为0。解题思路:直观上思考,可以先从起点走到终点,计算最大路径和,并记录走过的所有点,然后把所有点的数值置为0,再从起点走到终点,计算最大路径和,把两次的最大路径......
  • 洛谷 P10084 [GDKOI2024 提高组] 计算
    洛谷传送门第一步是一个经典结论,\(L=m^{\gcd(a,b)}+1\),\(R=m^{\gcd(c,d)}\)。因为\(L\equiv1\pmodm\)且\(R\equiv0\pmodm\),所以可以把问题的范围改成\([1,n=R-L+1]\)。写出选数的生成函数:\[F(x)=\prod\limits_{i=1}^n(1+x^i)\]我们希望求......
  • 题解:洛谷 P1137 旅行计划
    标签:图论,拓扑,dp题意给定一张\(n\)个点\(m\)条边的DAG,对于每个\(i\),求以它为终点最多经过多少个点?思路由于是DAG,求的是终点\(i\)经过的所有点,而刚好拓扑序就满足这个。那么就可以考虑拓扑排序。设\(f_i\)是以\(i\)为终点的最多结点数,那么就有转移方程\(f_v=m......
  • 洛谷P5597 复读
    洛谷P5597复读首先概括一下题意:文中给出三个指令L(命令机器人向当前节点的左子节点走)、R(命令机器人向当前节点的右子节点走)、U(命令机器人向当前节点的父亲节点走)以及一颗二叉树。初始状态,机器人在二叉树的根节点。当机器人处于二叉树的根节点时,不可以执行指令U,但是机器人可以走......
  • 「洛谷」题解:P1008 三连击
    题目传送门题目背景本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。题目描述将\(1,2,\ldots,9\)共\(9\)个数分成\(3\)组,分别组成\(3\)个三位数,且使这\(3\)个三位数构成\(1:2:3\)的比例,试求出所有满足条件的......
  • 洛谷题单指南-动态规划2-P2679 [NOIP2015 提高组] 子串
    原题链接:https://www.luogu.com.cn/problem/P2679题意解读:在a中按顺序挑选k个子串,使得这些子串连在一起正好和b相等,求方案数。解题思路:这样的题目,无外乎两个思路:DFS暴搜(得部分分)、动态规划动态规划不好想,还是先暴搜吧!1、DFS暴搜搜索的思路就是:从a起始位置开始,一直找到跟b前......
  • [题解] [洛谷P4158] 粉刷匠
    [题解][洛谷P4158]粉刷匠题目描述有\(n\)个木板,每个木板有\(m\)个格子,所有格子最开始视为没有颜色。有\(0/1\)两种颜色,每次可以粉刷其中一块木板上一段连续的格子,总共可以粉刷\(t\)次。给出一组目标颜色,问最多可以将多少个格子粉刷成目标颜色。输入格式第一行包含......
  • 洛谷题单指南-动态规划2-P1439 【模板】最长公共子序列
    原题链接:https://www.luogu.com.cn/problem/P1439题意解读:求最长公共子序列的长度。解题思路:1、O(n^2)的方法:动态规划设两个排列为a,b设dp[i][j]表示a[1~i]与b[1~j]的最长公共子序列长度根据公共子序列结尾是否包含a[i]、b[j]是否相等分情况讨论:如果a[i]==b[j]  则结尾......