51Nod1868 彩色树 虚树
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1868.html
题目传送门 - 51Nod1868
题意
给定一颗 $n$个点的树,每个点一个 $[1,n]$ 的颜色。设 $g(x,y)$ 表示 $x$ 到 $y$ 的树上路径上有几种颜色。
对于一个长度为 $n$ 的排列 $P[1\cdots n]$ ,定义 $f(P)=\sum_{i=1}^{n-1}g(P_i,P_{i+1})$ 。
现在求对于 $n!$ 个排列,他们的 $f(P)$ 之和 对 $10^9+7$ 取模后的值。
题解
首先我们考虑每一个 $g(x,y)$ 对于答案的贡献次数。
考虑捆绑法,把 $x$ 和 $y$ 看作一个整体,显然,它对答案的贡献次数为 $(n-1)!$ 。
于是答案就是
$$2\times (n-1)!\sum_{x=1}^{n}\sum_{y=x+1}^{n} g(x,y)$$
前面的 $2\times (n-1)!$ 很好办,现在主要要求后面的那个。
我们考虑对于每一个颜色分别处理。我们需要求出每一个颜色对答案的贡献。
记 $f(c,x,y)$ 表示路径 $x$~$y$ 上,如果有颜色 $c$ ,那么值为 $1$ ,否则为 $0$ 。则后面一半变成了:
$$\sum_{c=1}^{n}\sum_{x=1}^{n}\sum_{y=x+1}^{n} f(c,x,y)$$
确定一种颜色之后,后面的显然非常好求,直接一个树形dp 就差不多了。但是这样的时间复杂度是炸掉的。于是我们需要一个数据结构来优化——虚树。
建出虚树,然后我们注意一下细节,统计一下就可以了。
这里推荐一个写的比较详细的虚树学习笔记:https://www.k-xzy.xyz/archives/4476
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005,mod=1e9+7;
int read(){
int x=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x;
}
struct Gragh{
static const int M=N*2;
int cnt,y[M],nxt[M],fst[N];
void clear(){
cnt=0;
memset(fst,0,sizeof fst);
}
void add(int a,int b){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
}
}g,t;
int n,c[N],Fac[N],Time=0,now_color,ans=0;
int dfn[N],depth[N],size[N],fa[N][18],sqrsum[N];
int dirson[N],tot[N],st[N],top;
vector <int> id[N];
LL calc(int x){
return 1LL*x*(x-1)/2;
}
void dfs(int x,int pre,int d){
dfn[x]=++Time,depth[x]=d,size[x]=1,fa[x][0]=pre,sqrsum[x]=0;
for (int i=1;i<18;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for (int i=g.fst[x];i;i=g.nxt[i])
if (g.y[i]!=pre){
int y=g.y[i];
dfs(y,x,d+1);
size[x]+=size[y];
sqrsum[x]=(calc(size[y])+sqrsum[x])%mod;
}
}
int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
for (int i=17;i>=0;i--)
if (depth[x]-(1<<i)>=depth[y])
x=fa[x][i];
if (x==y)
return x;
for (int i=17;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
bool cmp(int a,int b){
return dfn[a]<dfn[b];
}
void solve(int x){
int dx=dirson[x],sonsqr=tot[x]=0;
for (int k=t.fst[x];k;k=t.nxt[k]){
int y=t.y[k],&dy=dirson[y]=y;
for (int i=17;i>=0;i--)
if (depth[dy]-(1<<i)>depth[x])
dy=fa[dy][i];
solve(y);
tot[x]+=tot[y];
sonsqr=(calc(tot[y])+sonsqr)%mod;
}
if (c[x]==now_color){
tot[x]=size[x];
int xsqr=(calc(size[dx]-size[x])+sqrsum[x])%mod;
ans=(calc(size[dx])-xsqr+ans)%mod;
}
else {
ans=(calc(tot[x])-sonsqr+ans)%mod;
for (int i=t.fst[x];i;i=t.nxt[i]){
int y=t.y[i],v=size[dx]-tot[x]+tot[y]-size[dirson[y]];
ans=(1LL*tot[y]*v+ans)%mod;
}
}
}
int main(){
n=read();
for (int i=Fac[0]=1;i<=n;i++)
c[i]=read(),Fac[i]=1LL*Fac[i-1]*i%mod;
g.clear();
for (int i=1;i<n;i++){
int a=read(),b=read();
g.add(a,b);
g.add(b,a);
}
dfs(1,0,0);
for (int i=1;i<=n;i++)
id[i].clear();
for (int i=1;i<=n;i++)
id[c[i]].push_back(i);
t.clear();
for (int k=1;k<=n;k++){
if (id[k].size()<1)
continue;
sort(id[k].begin(),id[k].end(),cmp);
st[top=1]=1,t.fst[1]=0;
for (vector <int> :: iterator i=id[k].begin();i!=id[k].end();i++){
int x=*i;
if (x==1)
continue;
int lca=LCA(x,st[top]);
if (lca!=st[top]){
while (depth[st[top-1]]>depth[lca])
t.add(st[top-1],st[top]),top--;
if (st[top-1]!=lca)
t.fst[lca]=0,t.add(lca,st[top]),st[top]=lca;
else
t.add(lca,st[top--]);
}
t.fst[x]=0,st[++top]=x;
}
for (int i=1;i<top;i++)
t.add(st[i],st[i+1]);
now_color=k,dirson[1]=1;
solve(1);
}
printf("%d\n",2LL*(ans+mod)%mod*Fac[n-1]%mod);
return 0;
}
51Nod1868 彩色树 虚树的更多相关文章
- 仙人掌 && 圆方树 && 虚树 总结
仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...
- [SDOI2018]战略游戏(圆方树+虚树)
喜闻乐见的圆方树+虚树 图上不好做,先建出圆方树. 然后答案就是没被选到的且至少有两条边可以走到被选中的点的圆点的数量. 语文不好,但结论画画图即可得出. 然后套路建出虚树. 发现在虚树上DP可以得出 ...
- hihoCoder #1954 : 压缩树(虚树)
题意 有一棵 \(n\) 个节点且以 \(1\) 为根的树,把它复制成 \(m\) 个版本,有 \(q\) 次操作,每次对 \([l, r]\) 这些版本的 \(v\) 节点到根的路径收缩起来. 收缩 ...
- Codechef Sad Pairs——圆方树+虚树+树上差分
SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ...
- BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)
Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...
- Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树
https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...
- BZOJ.5329.[SDOI2018]战略游戏(圆方树 虚树)
题目链接 显然先建圆方树,方点权值为0圆点权值为1,两点间的答案就是路径权值和减去起点终点. 对于询问,显然可以建虚树.但是只需要计算两关键点间路径权值,所以不需要建出虚树.统计DFS序相邻的两关键点 ...
- UOJ.87.mx的仙人掌(圆方树 虚树)(未AC)
题目链接 本代码10分(感觉速度还行..). 建圆方树,预处理一些东西.对询问建虚树. 对于虚树上的圆点直接做:对于方点特判,枚举其所有儿子,如果子节点不在该方点代表的环中,跳到那个点并更新其val, ...
- 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】
题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...
随机推荐
- Day8--------------RPM包管理
nginx.tar.gz:源码,编译安装 RPM:redhat package manage,二进制 增.删.查 1.增 package------>ls|less------------> ...
- SQL Server管理员必备技能之性能优化
SQL Server管理员必备技能之性能优化 高文龙关注1人评论1171人阅读2017-09-22 08:27:41 SQL Server 作为企业必不可少的服务之一,所以对于管理员的日常运维是一个极 ...
- eclipse 安装教程
eclipse 安装教程 一:安装包下载: 链接: https://pan.baidu.com/s/1qZtt62o 密码: 4ak2 注:若 下载链接失效,请看本文公告的QQ群,请联系群主. 二:安 ...
- Confluence 6 查看内容索引概要
内容索引,通常也被称为查找索引,这个索引被用来在 Confluence 中支持查找.这个索引同时也被其他的一些功能使用,例如在归档邮件中构建邮件主题,View Space Activity 的特性和将 ...
- Confluence 6 从其他备份中恢复数据
一般来说,Confluence 数据库可以从 Administration Console 或者 Confluence Setup Wizard 中进行恢复. 如果你在恢复压缩的 XML 备份的时候遇 ...
- 【JS】中ajax的URL中包含中文,后台接收乱码
[问题]ajax提交get请求,url中参数包含中文,后台接收到显示乱码. [解决方案]前台: function getSiteInfoByName(siteName){ var res; $.aja ...
- SpringBoot集成前端模版(thymeleaf)
1.在application.properties配置文件中添加 thymeleaf 的配置信息 spring.datasource.driverClassName=com.mysql.jdbc.Dr ...
- day14 迭代器 生成器 面向过程思想
"" 迭代器 什么是迭代器(iterator) 器指的某种工具, 迭代指的是更新换代的过程,例如应用程序的版本更新从1.0 变成 1.1 再1.2 迭代的目的是要根据上一个结果,产 ...
- 第十七单元 Samba服务
Samba的功能 Samba的安装 Samba服务的启动.停止.重启 Samba服务的配置 Samba服务的主配置文件 samba服务器配置实例 Samba客户端设置 windows客户端 Linux ...
- django的查看sql语句setting设置
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'cons ...