@codechef - TREEPATH@ Decompose the Tree
@description@
给定一棵无根树,每个节点上都写了一个整数。
你的任务就是统计有多少种方法可以将这棵树分解为若干条路径,使得每个节点恰好属于一条路径,而且每条路径的节点上的数字之和非负。
输入格式
输入数据第一行包含一个整数 T,表示数据组数。
接下来是 T 组数据。 每组数据的第一行包含一个整数 N,代表树中结点个数。
接下来一行包含 N 个整数,由空格分隔,代表每个节点上写的数字。
接下来 N −1 行,每行包含两个整数 Xj 和 Yj,代表编号为 Xj 和 Yj 的节点之间有边直接相连。
输出格式
对于每组数据,输出一行,包含一个整数,即为将树分解的方案数对 10^9 +7 取模得到的结果。
数据范围与子任务
• 1 ≤ T ≤ 10^5
• 1 ≤ Xj,Yj ≤ N
样例数据
输入
1 4
1 10 5 -1
1 2
1 3
2 4
输出
4
样例解释
一共有 4 种分解方法:
• 整棵树即为一条路径,其和为 1 + 10 + 5 + (−1) = 15;
• 一条路径包含节点 2 和 4,其和为 10 + (−1) = 9;另一条路径包含节点 1 和 3,其和为 1 + 5 = 6;
• 一条路径包含节点1、2和4,其和为 1+10+(−1) = 10;另一条路径包含节点3,其和为 5;
• 第一条路径包含节点 2 和 4,其和为 10 + (−1) = 9;第二条路径包含节点 1,其和为 1;第 三条路径包含节点 3,其和为 3。
@solution@
首先可以想到一个朴素 dp:定义 dp[x] 表示分解 x 为根的子树的合法方案数。
转移时枚举一条以 x 为 lca 的非零链,将链所有支出去的枝丫的 dp 值乘起来即可。
这样子搞是 O(n^2),考虑优化。
我们考虑可以使用启发式合并 + 平衡树来优化我们枚举链的过程。
考虑处理完轻子树继承重子树,我们维护重子树中每个儿子向上爬到当前点的链权值和以及枝丫的 dp 值乘积。
每次通过打 tag 给链权值和加上当前点的 val,方案数乘上轻子树的 dp 值乘积。
然后考虑处理轻子树。先统计链的一个端点在当前轻子树,另一个端点维护在平衡树里的情况。
这个可以通过平衡树维护子树方案数之和 + 分裂成权值 < k 的与 >= k 的两棵平衡树,然后取 >= k 的平衡树的答案搞定。
然后就把这棵轻子树里所有的链丢进平衡树里。
最后加入当前根,记得再在平衡树取出权值和 >= 0 的链的方案数(即链的端点为当前点的情况)。
看起来非常完美,然而有一个问题:万一 dp 值为 0(可能本身没有方案也可能方案数太多 mod 10^9 + 7 = 0),是不存在逆元的。
当然解决方法很多,但是我选择最简单(最sb)的解决方法:判断它有多少的儿子的 dp 值为 0。
如果多于 2 个显然就全部方案数都为 0,否则分类讨论一大堆,然后 WA 到怀疑人生。。。
@accepted code@
#include<cstdio>
#include<queue>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int MOD = int(1E9) + 7;
inline int add(int a, int b) {return (a + b)%MOD;}
inline int mul(int a, int b) {return 1LL*a*b%MOD;}
int pow_mod(int b, int p) {
int ret = 1;
while( p ) {
if( p & 1 ) ret = 1LL*ret*b%MOD;
b = 1LL*b*b%MOD;
p >>= 1;
}
return ret;
}
struct edge{
edge *nxt; int to;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int siz[MAXN + 5], hvy[MAXN + 5];
void dfs1(int x, int f) {
siz[x] = 1, hvy[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs1(p->to, x);
siz[x] += siz[p->to];
if( siz[p->to] > siz[hvy[x]] )
hvy[x] = p->to;
}
}
struct Treap{
struct node{
node *ch[2];
int key, cnt, sum;
int tg1, tg2;
unsigned int pri;
}pl[MAXN + 5], *root, *NIL, *ncnt;
typedef pair<node*, node*> Droot;
Treap() {
ncnt = root = NIL = &pl[0];
NIL->ch[0] = NIL->ch[1] = NIL; NIL->sum = 0;
}
unsigned int get_rand() {
return (rand() << 16) | rand() ^ (rand() << 8);
}
node *newnode(int k, int c) {
node *f = (++ncnt);
f->ch[0] = f->ch[1] = NIL;
f->pri = get_rand(), f->key = k, f->cnt = f->sum = c;
f->tg1 = 0, f->tg2 = 1;
return f;
}
void pushdown(node *x) {
if( x->tg1 ) {
if( x->ch[0] != NIL )
x->ch[0]->tg1 += x->tg1, x->ch[0]->key += x->tg1;
if( x->ch[1] != NIL )
x->ch[1]->tg1 += x->tg1, x->ch[1]->key += x->tg1;
x->tg1 = 0;
}
if( x->tg2 != 1 ) {
if( x->ch[0] != NIL ) {
x->ch[0]->tg2 = mul(x->ch[0]->tg2, x->tg2);
x->ch[0]->cnt = mul(x->ch[0]->cnt, x->tg2);
x->ch[0]->sum = mul(x->ch[0]->sum, x->tg2);
}
if( x->ch[1] != NIL ) {
x->ch[1]->tg2 = mul(x->ch[1]->tg2, x->tg2);
x->ch[1]->cnt = mul(x->ch[1]->cnt, x->tg2);
x->ch[1]->sum = mul(x->ch[1]->sum, x->tg2);
}
x->tg2 = 1;
}
}
void pushup(node *x) {
x->sum = add(x->cnt, add(x->ch[0]->sum, x->ch[1]->sum));
}
node *merge(node *rt1, node *rt2) {
if( rt1 == NIL ) return rt2;
if( rt2 == NIL ) return rt1;
pushdown(rt1), pushdown(rt2);
if( rt1->pri < rt2->pri ) {
rt2->ch[0] = merge(rt1, rt2->ch[0]), pushup(rt2);
return rt2;
}
else {
rt1->ch[1] = merge(rt1->ch[1], rt2), pushup(rt1);
return rt1;
}
}
Droot split(node *rt, int k) {
if( rt == NIL ) return make_pair(NIL, NIL);
pushdown(rt);
if( k <= rt->key ) {
Droot tmp = split(rt->ch[0], k);
rt->ch[0] = tmp.second; pushup(rt);
return make_pair(tmp.first, rt);
}
else {
Droot tmp = split(rt->ch[1], k);
rt->ch[1] = tmp.first; pushup(rt);
return make_pair(rt, tmp.second);
}
}
void insert(node *x) {
Droot tmp = split(root, x->key);
root = merge(merge(tmp.first, x), tmp.second);
}
int query(int k) {
Droot tmp = split(root, k);
int ret = tmp.second->sum;
root = merge(tmp.first, tmp.second);
return ret;
}
void update1(int x) {
if( root != NIL )
root->tg1 += x, root->key += x;
}
void update2(int x) {
if( root != NIL )
root->tg2 = mul(root->tg2, x), root->cnt = mul(root->cnt, x), root->sum = mul(root->sum, x);
}
void init() {
ncnt = root = NIL;
}
}T;
int dp[MAXN + 5], spro[MAXN + 5], inv[MAXN + 5], pos[MAXN + 5], val[MAXN + 5];
int dfs3(int x, int f, int s, int k) {
int ret = 0;
if( !pos[x] ) ret = mul(T.query(-s), mul(k, spro[x]));
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( pos[x] ) {
if( pos[x] == p->to )
ret = add(ret, dfs3(p->to, x, s + val[p->to], mul(k, spro[x])));
}
else ret = add(ret, dfs3(p->to, x, s + val[p->to], mul(k, mul(spro[x], inv[p->to]))));
}
return ret;
}
void dfs4(int x, int f, int s, int k) {
if( !pos[x] ) T.insert(T.newnode(s, mul(k, spro[x])));
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( pos[x] ) {
if( pos[x] == p->to )
dfs4(p->to, x, s + val[p->to], mul(k, spro[x]));
}
else dfs4(p->to, x, s + val[p->to], mul(k, mul(spro[x], inv[p->to])));
}
}
void update(int x, int y) {
if( !dp[y] ) pos[x] = (pos[x] ? -1 : y);
else spro[x] = mul(spro[x], dp[y]);
}
void dfs2(int x, int f, bool flag) {
spro[x] = 1, pos[x] = 0, dp[x] = 0;
if( !hvy[x] ) {
inv[x] = dp[x] = (val[x] >= 0);
if( flag )
T.insert(T.newnode(val[x], 1));
return ;
}
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f || p->to == hvy[x] ) continue;
dfs2(p->to, x, false), update(x, p->to);
}
dfs2(hvy[x], x, true), update(x, hvy[x]);
T.update1(val[x]);
if( !pos[x] ) {
T.update2(mul(spro[x], inv[hvy[x]]));
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f || p->to == hvy[x] ) continue;
dp[x] = add(dp[x], dfs3(p->to, x, val[p->to], inv[p->to]));
dfs4(p->to, x, val[x] + val[p->to], mul(spro[x], inv[p->to]));
}
T.insert(T.newnode(val[x], spro[x]));
}
else {
if( pos[x] == hvy[x] ) {
T.update2(spro[x]);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f || p->to == hvy[x] ) continue;
dp[x] = add(dp[x], dfs3(p->to, x, val[p->to], inv[p->to]));
}
}
else {
if( pos[x] != -1 ) {
dp[x] = add(dp[x], dfs3(pos[x], x, val[pos[x]], mul(spro[x], inv[hvy[x]])));
T.update2(0);
dfs4(pos[x], x, val[x] + val[pos[x]], spro[x]);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f || p->to == hvy[x] || p->to == pos[x] ) continue;
dp[x] = add(dp[x], dfs3(p->to, x, val[p->to], inv[p->to]));
}
}
else {
int cnt = 0; bool flag = false;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( dp[p->to] == 0 ) {
cnt++;
if( p->to == hvy[x] )
flag = true;
}
}
if( cnt == 2 ) {
if( flag ) {
T.update2(spro[x]);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( dp[p->to] == 0 && p->to != hvy[x] )
dp[x] = add(dp[x], dfs3(p->to, x, val[p->to], 1));
}
}
else {
T.update2(0);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( dp[p->to] == 0 ) {
if( !flag ) {
dfs4(p->to, x, val[x] + val[p->to], spro[x]);
flag = true;
}
else dp[x] = add(dp[x], dfs3(p->to, x, val[p->to], 1));
}
}
}
}
T.update2(0);
}
}
}
dp[x] = add(dp[x], T.query(0)), inv[x] = pow_mod(dp[x], MOD-2);
if( !flag ) T.init();
}
void solve() {
int n; scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%d", &val[i]), adj[i] = NULL;
ecnt = &edges[0];
for(int i=1;i<n;i++) {
int x, y; scanf("%d%d", &x, &y);
addedge(x, y);
}
int rt = T.get_rand() % n + 1;
dfs1(rt, 0), dfs2(rt, 0, false);
printf("%d\n", dp[rt]);
}
int main() {
int t; scanf("%d", &t);
srand(20041112^t);
while( t-- ) solve();
}
@details@
好久没写非旋 treap 了(?写过吗),在 merge 的时候写成了按根的权值决定左右儿子关系。。。
因为只有堆才满足根的权值最大,可以只比较根的权值。。。要是两棵平衡树根的权值一样不就炸了。。。
正确写法是在 merge 时不考虑权值,在调用 merge 时保证两棵平衡树的相对大小关系即可。
@codechef - TREEPATH@ Decompose the Tree的更多相关文章
- BZOJ 3221: [Codechef FEB13] Obserbing the tree树上询问( 可持久化线段树 + 树链剖分 )
树链剖分+可持久化线段树....这个一眼可以看出来, 因为可持久化所以写了标记永久化(否则就是区间修改的线段树的持久化..不会), 结果就写挂了, T得飞起...和管理员拿数据调后才发现= = 做法: ...
- codechef Prime Distance On Tree(树分治+FFT)
题目链接:http://www.codechef.com/problems/PRIMEDST/ 题意:给出一棵树,边长度都是1.每次任意取出两个点(u,v),他们之间的长度为素数的概率为多大? 树分治 ...
- Codechef:Path Triples On Tree
Path Triples On Tree 题意是求树上都不相交或者都相交的路径三元组数量. 发现blog里没什么树形dp题,也没有cc题,所以来丢一道cc上的树形dp题. 比较暴力,比较恶心 #inc ...
- [BZOJ 3221][Codechef FEB13] Obserbing the tree树上询问
[BZOJ 3221]Obserbing the tree树上询问 题目 小N最近在做关于树的题.今天她想了这样一道题,给定一棵N个节点的树,节点按1~N编号,一开始每个节点上的权值都是0,接下来有M ...
- Codechef TSUM2 Sum on Tree 点分治、李超线段树
传送门 点分治模板题都不会迟早要完 发现这道题需要统计所有路径的信息,考虑点分治统计路径信息. 点分治之后,因为路径是有向的,所以对于每一条路径都有向上和向下的两种.那么如果一条向上的路径,点数为\( ...
- 【CodeChef】QTREE- Queries on tree again!
题解 给你一棵基环树,环长为奇数(两点间最短路径只有一条) 维护两点间路径最大子段和,支持把一条路径上的值取反 显然只要断开一条边维护树上的值,然后对于那条边分类讨论就好了 维护树上的值可以通过树链剖 ...
- codechef T6 Pishty and tree dfs序+线段树
PSHTTR: Pishty 和城堡题目描述 Pishty 是生活在胡斯特市的一个小男孩.胡斯特是胡克兰境内的一个古城,以其中世纪风格 的古堡和非常聪明的熊闻名全国. 胡斯特的镇城之宝是就是这么一座古 ...
- Codechef Prime Distance On Tree
[传送门] FFT第四题! 暑假的时候只会点分,然后合并是暴力合并的...水过去了... 其实两条路径长度的合并就是卷积的过程嘛,每次统计完路径就自卷积一下. 刚开始卷积固定了值域.T了.然后就不偷懒 ...
- @codechef - SONATR@ Sonya and Tree
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定 p 为 0~N-1 的一个排列,并给定一棵 N 个点的树. ...
随机推荐
- 【JZOJ5338】【NOIP2017提高A组模拟8.25】影子 点分治?/ 排序
题面 65 看到路径问题,就想到了套路:点分治. 对于一个分治中心,先把在其子树的结点的sum和mn求出来,分别表示该节点到分治中心的边权和和点权最小值. 然后把mn离散化,并插入权值线段树中,以su ...
- linux追加中文字库,解决imagemagick 中文乱码的问题。
Windows下的字体丰富多样,而且显示的工整.漂亮. 所以自己想把windows上的字体移到Ubuntu下来.Windows下字体库的位置为C:\Windows\fonts,这里面包含所有windo ...
- LINUX常见服务列表
服务名 必需(是/否)用途描述 注解 acon 否 语言支持 特别支持左手书写语言:阿拉伯语,波斯语和希伯莱语 acpi ...
- 洛谷P1569属牛的抗议 超级强力无敌弱化版
P1569 [USACO11FEB]属牛的抗议Generic Cow Prote- 题目描述 约翰家的N头奶牛聚集在一起,排成一列,正在进行一项抗议活动.第i头奶牛的理智度 为Ai,Ai可能是负数.约 ...
- 浅谈mybatis中#{}和${}的区别
#{}:表示占位符,如果获取简单类型,#{}中可以使用value或其它名称.有效防止sql注入.使用#{}设置参数无需考虑参数的类型. 如果使用#{}比较日期字段,select* from table ...
- 痞子衡嵌入式:飞思卡尔i.MX RTyyyy系列MCU外设那些事(2)- 善变的FlexRAM
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RTyyyy系列MCU的FlexRAM外设. 本文是外设系列第二篇,上一篇讲的是离内核最近的高速缓存L1 Cache, ...
- [新手必看] 17个常见的Python运行时错误
对于刚入门的Pythoner在学习过程中运行代码是或多或少会遇到一些错误,刚开始可能看起来比较费劲.随着代码量的积累,熟能生巧当遇到一些运行时错误时能够很快的定位问题原题.下面整理了常见的17个错误, ...
- JSP Web第五章整理复习 JSP访问数据库
P164 例5-1 常用SQL语句 P178 数据库连接池 (1)连接池的作用 存储多个数据库连接对象,当程序需要时,从池中获取1个连接,程序执行完成后再还给连接池.避免数据库连接建立.关闭的开 ...
- golang之常量
1. 常量可以是全局常量,也可以是函数内部的局部常量.常量的值不可修改,常量表达式的值在编译期计算,而不是在运行期.存储在常量中的数据类型只可以是布尔型.数字型(整数型.浮点型和复数)和字符串型.当 ...
- 春蔚专访--MaxCompute 与 Calcite 的技术和故事
摘要:2019大数据技术公开课第一季<技术人生专访>,来自阿里云计算平台事业部高级开发工程师雷春蔚向大家讲述了MaxCompute 与 Calcite 的技术和故事. 具体内容包括: 1) ...