odds
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 250 Accepted Submission(s): 72
Problem Description
度度熊有一棵 N 个节点 (node) 的有根树 (rooted tree),树上的每条边 (edge) 都有一个整数的权重,对于每一个非叶的节点 (non-leaf node),通往子节点 (child) 的所有边的权重总和为 2×105。
考虑以下在树上行走的随机过程:
1. 起始位置在根节点。
2. 如果现在位置在任何一个叶节点 (leaf node) 上,则结束。
3. 令现在所在的节点通往子节点i的边为 ei,其权重为 vi。
4. 定义 P(ei) 为选择到边 ei 的概率,以 P(ei)=vi/(2×105) 的概率分布随机选择一条边,移动至对应的子节点。
5. 跳至第 2. 步骤。
接下来我们定义以下的操作:
1. 选定一个非叶节点
2. 将这个节点所有通往子节点的边的权重随意重新排序,亦即可以无限次地交换彼此之间的权重,但不能改变权重的大小。
给定一个固定的 X 值,请对于每个叶节点分开考虑以下问题:
如果能进行至多 X 次的操作,那到达这个叶节点的概率最大可以多大呢?
考虑以下在树上行走的随机过程:
1. 起始位置在根节点。
2. 如果现在位置在任何一个叶节点 (leaf node) 上,则结束。
3. 令现在所在的节点通往子节点i的边为 ei,其权重为 vi。
4. 定义 P(ei) 为选择到边 ei 的概率,以 P(ei)=vi/(2×105) 的概率分布随机选择一条边,移动至对应的子节点。
5. 跳至第 2. 步骤。
接下来我们定义以下的操作:
1. 选定一个非叶节点
2. 将这个节点所有通往子节点的边的权重随意重新排序,亦即可以无限次地交换彼此之间的权重,但不能改变权重的大小。
给定一个固定的 X 值,请对于每个叶节点分开考虑以下问题:
如果能进行至多 X 次的操作,那到达这个叶节点的概率最大可以多大呢?
Input
输入的第一行有一个正整数 T,代表接下来有几组测试数据。
对于每组测试数据:
第一行有两个整数 N, X。
接下来的 N−1 行中,每行有三个整数 a, b 以及 v,代表这颗树上节点 a 至节点 b 有一条权重为 v 的边,同时,保证 b 为 a 的子节点。
节点的编号由 0 至 N−1,节点 0 为树根。
* 2≤N≤105
* 0≤X≤N
* 0≤a,b<N
* 0≤v≤ 2×105
* 节点 b 为 a 的子节点
* 对于非叶节点,其所有通往子节点的边的权重总合為 2×105
* 1≤T≤21
* 至多 2 组测试数据中的 N>2000
对于每组测试数据:
第一行有两个整数 N, X。
接下来的 N−1 行中,每行有三个整数 a, b 以及 v,代表这颗树上节点 a 至节点 b 有一条权重为 v 的边,同时,保证 b 为 a 的子节点。
节点的编号由 0 至 N−1,节点 0 为树根。
* 2≤N≤105
* 0≤X≤N
* 0≤a,b<N
* 0≤v≤ 2×105
* 节点 b 为 a 的子节点
* 对于非叶节点,其所有通往子节点的边的权重总合為 2×105
* 1≤T≤21
* 至多 2 组测试数据中的 N>2000
Output
对于每一组测试数据,对于每一个叶节点,请依序在一行中输出一个整数 V,如果这个节点的答案的最简有理数为 P/Q,则 V=P×Q−1 mod 109+7。
请按照节点编号的顺序由小至大输出。
两组测试数据间请不要输出多余的空白行。
在本题中,对于正整数 Q,定义Q−1 的值为一正整数 Q′ 滿足 Q′<109+7 且 Q×Q′mod 109+7 = 1。
请按照节点编号的顺序由小至大输出。
两组测试数据间请不要输出多余的空白行。
在本题中,对于正整数 Q,定义Q−1 的值为一正整数 Q′ 滿足 Q′<109+7 且 Q×Q′mod 109+7 = 1。
Sample Input
1
11 2
0 1 100
0 2 199700
0 3 200
1 4 120000
1 5 80000
3 6 100000
3 7 100000
7 8 90000
7 9 90000
7 10 20000
Sample Output
714500006
628700005
628700005
357250003
110762501
110762501
110762501
Source
Recommend
析:这个题目,我差不多是暴力加了点剪枝过去的,但是速度挺快的,在比赛时才用了100ms多,说一下我的理解,如果某一个叶子的最大概率,相信很简单就能求出来,只要把从根结点到该叶子结点的所有的结点的值和最大值保存下来,然后按比值排个序就能够知道哪 x 个要重排,这是一个叶子的求法,但是如果很多呢,我们可以进行计算,叶子多了那么树的深度就会降低,也就是从根结点到叶子的结点数量会减少,但是肯定有一个最大值,两者都比较大,这个值不知道后台是什么数据。然后如果对每个结点都进行回溯的话,不幸的是,这样肯定超时,我们可以考虑记录一条路径,也可以维护一个栈,这样到每个叶子以前的结点都记录下来了,只要在叶子结点再重新赋值,再排序一次就好了,但是还是超时了。我考虑到可以把当前和重排后的最大值相等的结点去掉,这个去掉我一开始只是没有加入排序,这样的话,每次排序会少一些结点,并且如果某个叶结点的深度不超过 x ,那么这个值就可以直接选每个结点的最大值。这样还是超时了。最后一个优化就是把当前和重排后的最大值相等的结点在前面就计算出来,不是留到最后再计算,放到后面再计算虽然不用排序,但是还是算,同时更新不是树的深度小于等于 x 就加最大值,而是根据路径里的结点数量来确定。就这样我就 A 掉这个题目,我觉得这肯定不是正解,官方题解好多繁体字就没有看。。。
代码如下:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <iostream> #include <cstring> #include <set> #include <queue> #include <algorithm> #include <vector> #include <map> #include <cctype> #include <cmath> #include <stack> #include <sstream> #include <list> #include <assert.h> #include <bitset> #include <numeric> #define debug() puts("++++") #define gcd(a, b) __gcd(a, b) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define fi first #define se second #define pb push_back #define sqr(x) ((x)*(x)) #define ms(a,b) memset(a, b, sizeof a) #define sz size() #define be begin() #define ed end() #define pu push_up #define pd push_down #define cl clear() #define lowbit(x) -x&x //#define all 1,n,1 #define FOR(i,n,x) for(int i = (x); i < (n); ++i) #define freopenr freopen("in.in", "r", stdin) #define freopenw freopen("out.out", "w", stdout) using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> P; const int INF = 0x3f3f3f3f; const LL LNF = 1e17; const double inf = 1e20; const double PI = acos(-1.0); const double eps = 1e-8; const int maxn = 1e5 + 10; const int maxm = 1e6 + 10; const LL mod = 1000000007LL; const int dr[] = {-1, 1, 0, 0, 1, 1, -1, -1}; const int dc[] = {0, 0, 1, -1, 1, -1, 1, -1}; const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"}; int n, m; const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; inline bool is_in(int r, int c) { return r >= 0 && r < n && c >= 0 && c < m; } inline int readInt(){ int x; scanf("%d", &x); return x; } const LL inv = 714285005LL; LL fast_pow(LL a, int n){ LL res = 1; while(n){ if(n&1) res = res * a % mod; n >>= 1; a = a * a % mod; } return res; } vector<P> G[maxn]; int val[maxn]; int ans[maxn]; int pd[maxn]; struct Node{ int a, b; Node() {} Node(int a_, int b_) : a(a_), b(b_) { } bool operator < (const Node &rhs) const{ return (LL)b * rhs.a > (LL)a * rhs.b; } }; vector<Node> path(maxn); vector<Node> res; void dfs(int u, int d, int len, LL all){ if(G[u].empty()){ // the leaf node LL sum = all; if(m >= len){ for(int i = 0; i < len; ++i) sum = sum * path[i].b % mod; ans[u] = (int)(sum * pd[d] % mod); return ; } res.cl; res.assign(path.be, path.be+len); sort(res.be, res.ed); for(int i = 0; i < m; ++i) sum = sum * res[i].b % mod; for(int i = m; i < len; ++i) sum = sum * res[i].a % mod; ans[u] = (int)(sum * pd[d] % mod); return ; } for(int i = 0; i < G[u].sz; ++i){ if(G[u][i].se == val[u]) dfs(G[u][i].fi, d + 1, len, all * val[u] % mod); else{ path[len] = Node(G[u][i].se, val[u]); dfs(G[u][i].fi, d + 1, len + 1, all); } } } int main(){ pd[0] = 1; for(int i = 1; i < maxn; ++i) pd[i] = (LL)pd[i-1] * inv % mod; int T; cin >> T; while(T--){ scanf("%d %d", &n, &m); for(int i = 0; i < n; ++i) G[i].cl, val[i] = ans[i] = -1; int a, b, c; for(int i = 1; i < n; ++i){ scanf("%d %d %d", &a, &b, &c); G[a].pb(P(b, c)); val[a] = max(val[a], c); } dfs(0, 0, 0, 1); for(int i = 0; i < n; ++i) if(ans[i] != -1) printf("%d\n", ans[i]); } return 0; }