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\)的情 ...
随机推荐
- POJ 2115
ax=b (mod n) 该方程有解的充要条件为 gcd(a,n) | b ,即 b% gcd(a,n)==0 令d=gcd(a,n) 有该方程的 最小整数解为 x = e (mod n/d) 其中e ...
- Bootstrap的插件
04-Bootstrap的插件 1.下拉菜单 代码如下: <div class="dropdown"> <button class="btn btn ...
- GoLand使用
# 不定期更新 什么是GoLand GoLand是JetBrains出品的一个Go语言IDE,JB的IDE有多好用我想很多程序员都知道,个人感觉唯一的缺点就是比较大(因为功能多) 希望大家多多支持正版 ...
- Codeforces 833D Red-Black Cobweb [点分治]
洛谷 Codeforces 思路 看到树上路径的统计,容易想到点分治. 虽然只有一个限制,但这个限制比较麻烦,我们把它拆成两个. 设黑边有\(a\)条,白边有\(b\)条,那么有 \[ 2a\geq ...
- mybatis 按in 函数参数顺序排序
使用 FIELD()函数 SELECT * FROM user WHERE id IN (72, 80, 69) ORDER BY FIELD(id, 72, 80, 69)
- python 排序 sort和sorted
当我们从数据库中获取一写数据后,一般对于列表的排序是经常会遇到的问题,今天总结一下python对于列表list排序的常用方法: 第一种:内建方法sort() 可以直接对列表进行排序 用法: list. ...
- 用sqlplus为oracle创建用户和表空间
用Oracle自带的企业管理器或PL/SQL图形化的方法创建表空间和用户以及分配权限是相对比较简单的, 本文要介绍的是另一种方法就是使用Oracle所带的命令行工具SQLPLUS来创建表空间. 打开S ...
- Confluence 6 Windows 中以服务方式自动重启为服务手动安装 Confluence 分发包
在 Windows: 打开一个命令输入框,然后修改目录到 <CONFLUENCE-INSTALL>/bin 目录中.你需要以管理员权限运行这个命令行输入框(Run as administr ...
- IO 多路复用
IO 多路复用 多路复用也是要用单线程来处理客户端并发,与其他模型相比多出了select这个模块. 程序不再直接问操作系统要数据,而是先发起一个select调用,select会阻塞直到其中某个sock ...
- 《剑指offer》 数值的整数次方
本题来自<剑指offer> 数值的整数次方 题目: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 思路: 代码从三个方面处 ...