2017 ACM-ICPC, Universidad Nacional de Colombia Programming Contest K - Random Numbers (dfs序 线段树+数论)...

Tamref love random numbers, but he hates recurrent relations, Tamref thinks that mainstream random generators like the linear congruent generator suck. That's why he decided to invent his own random generator.

As any reasonable competitive programmer, he loves trees. His generator starts with a tree with numbers on each node. To compute a new random number, he picks a rooted subtree and multiply the values of each node on the subtree. He also needs to compute the number of divisors of the generated number (because of cryptographical applications).

In order to modify the tree (and hence create different numbers on the future), Tamref decided to perform another query: pick a node, and multiply its value by a given number.

Given a initial tree T, where Tu corresponds to the value on the node u, the operations can be summarized as follows:

  • RAND: Given a node u compute and count its divisors, where T(u) is the set of nodes that belong to the subtree rooted at u.
  • SEED: Given a node u and a number x, multiply Tu by x.

Tamref is quite busy trying to prove that his method indeed gives integers uniformly distributed, in the meantime, he wants to test his method with a set of queries, and check which numbers are generated. He wants you to write a program that given the tree, and some queries, prints the generated numbers and count its divisors.

Tamref has told you that the largest prime factor of both Tu and x is at most the Tamref's favourite prime: 13. He also told you that the root of T is always node 0.

The figure shows the sample test case. The numbers inside the squares are the values on each node of the tree. The subtree rooted at node 1 is colored. The RAND query for the subtree rooted at node 1 would generate 14400, which has 63 divisors.

Input

The first line is an integer n (1 ≤ n ≤ 105), the number of nodes in the tree T. Then there are n - 1 lines, each line contains two integers u and v (0 ≤ u, v < n) separated by a single space, it represents that u is a parent of v in T. The next line contains n integers, where the i - th integer corresponds to Ti (1 ≤ Ti ≤ 109). The next line contains a number Q (1 ≤ Q ≤ 105), the number of queries. The final Q lines contain a query per line, in the form "RAND u" or "SEED u x" (0 ≤ u < n, 1 ≤ x ≤ 109).

Output

For each RAND query, print one line with the generated number and its number of divisors separated by a space. As this number can be very long, the generated number and its divisors must be printed modulo 109 + 7.

Example

Input
8
0 1
0 2
1 3
2 4
2 5
3 6
3 7
7 3 10 8 12 14 40 15
3
RAND 1
SEED 1 13
RAND 1
Output
14400 63
187200 126

题意:给一颗树共n个点,以及其结点的数值ai,有q次行为,查询询问子树(包括节点)的数值的积,与更新单个节点即乘题给数(一开始以为是整个子树都要更新)


思路:明显的线段树题,但是问题是线段树里存的是什么。如果直接存积,数组开long long也存不下。那么就需要换种思路,存积的质因子的指数。

即把积X=(p1^a)*(p2^b)*(p3^c)*······中的a,b,c用数组记录。又题目给出子树结点的积可以用不超过13的素数的积来表示。则定义一个b[]={2,3,5,7,11,13}。

就能表示所有子树积。这样查询到以后可以直接用快速幂求得第一问,用乘法原理因子数=(a+1)*(b+1)······即得第二问。

想到这里剩下的操作就是套+改线段树dfs序板子了(这里膜一下月老tql)

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 #include<vector>
  6 #include<queue>
  7 #include<cmath>
  8 #include<set>
  9 #define lid id<<1
 10 #define rid id<<1|1
 11 #define INF 0x3f3f3f3f
 12 #define LL long long
 13 #define debug(x) cout << "[" << x << "]" << endl
 14 using namespace std;
 15 const int maxn = 1e5+5;
 16 int b[]={2,3,5,7,11,13};
 17 int d[maxn][6]={0};
 18 void cal(int x, int *d)
 19 {
 20     for(int i = 0;i < 6 ;i++){
 21         while(x%b[i]==0)x/=b[i],d[i]++;
 22     }
 23 }
 24 const int mx = 1e5+10;
 25 const int mod = 1e9+7;
 26 int L[mx], R[mx], p[mx];
 27 struct tree{
 28     int l, r;
 29     int p[6]; //2,3,5,7,11,13
 30     int lazy[6];
 31 }tree[mx<<2];
 32 vector<int> G[mx];
 33 int cnt;
 34 LL Ans[6] = {0};
 35 
 36 LL qpow(LL x, LL n){ //x^n
 37     LL res = 1;
 38     while (n > 0){
 39         if (n & 1) res = res*x%mod;
 40         x = x*x % mod;
 41         n >>= 1;
 42     }
 43     return res;
 44 }
 45 
 46 void push_up(int id){
 47     for (int i = 0; i < 6; i++)
 48         tree[id].p[i] = tree[lid].p[i]+tree[rid].p[i];
 49 }
 50 
 51 void build(int l, int r, int id){
 52     tree[id].l = l;
 53     tree[id].r = r;
 54     for (int i = 0; i < 6; i++) tree[id].p[i] = 0;
 55     if (l == r) return;
 56     int mid = (l+r) >> 1;
 57     build(l, mid, lid);
 58     build(mid+1, r, rid);
 59 }
 60 
 61 void dfs(int u){
 62     L[u] = ++cnt;
 63     int len = G[u].size();
 64     for (int i = 0; i < len; i++){
 65         int v = G[u][i];
 66         dfs(v);
 67     }
 68     R[u] = cnt;
 69 }
 70 
 71 void upd(int c, int id, int *x){
 72     if (tree[id].l == c && tree[id].r == c){
 73         for (int i = 0; i < 6; i++)
 74             tree[id].p[i] += x[i];
 75         return;
 76     }
 77     int mid = (tree[id].l + tree[id].r)>>1;
 78     if (c <= mid) upd(c, lid, x);
 79     else upd(c, rid, x);
 80     push_up(id);
 81 }
 82 
 83 void query(int l, int r, int id){
 84     if (tree[id].l == l && tree[id].r == r){
 85         for (int i = 0; i < 6; i++)
 86             Ans[i] += tree[id].p[i];
 87         return;
 88     }
 89     int mid = (tree[id].l + tree[id].r)>>1;
 90     if (r <= mid) query(l, r, lid);
 91     else if (mid < l) query(l, r, rid);
 92     else {
 93         query(l, mid, lid);
 94         query(mid+1, r, rid);
 95     }
 96 }
 97 
 98 int main(){
 99     int n, u, v, a, q;
100     cnt = 0;
101     scanf("%d", &n);
102     for (int i = 1; i < n; i++){
103         scanf("%d%d", &u, &v);
104         G[u].push_back(v);
105         p[v] = u;
106     }
107     for (int i = 0; i < n; i++){
108         if (!p[i]) {
109             dfs(i);
110             break;
111         }
112     }
113     build(1, n, 1);
114     for (int i = 0; i < n; i++){
115         scanf("%d", &a);
116         cal(a,d[i]);
117         upd(L[i], 1, d[i]);
118     }
119     scanf("%d", &q);
120     while (q--){
121         char s[10];
122         int d2[6] = {0};
123         scanf("%s%d", s, &a);
124         if (s[0] =='R'){
125             memset(Ans, 0, sizeof Ans);
126             query(L[a], R[a], 1);
127             LL ans = 1;
128             LL num = 1;
129             for (int i = 0; i < 6; i++){
130                 num = (num*qpow(b[i], Ans[i]))%mod;
131                 ans = ans*(Ans[i]+1)%mod;
132             }
133             printf("%lld %lld\n", num, ans);
134         }
135         else {
136             int c;
137             scanf("%d", &c);
138             cal(c, d2);
139             upd(L[a], 1, d2);
140         }
141     }
142     return 0;
143 }
View Code

 

 

转载于:https://www.cnblogs.com/llllrj/p/9502071.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM-ICPC(国际大学生程设计竞赛)是一项面向大学生的计算机编程竞赛,涉及算法和数据结构等领域。在比赛中,选手需要解决一系列编程问题,使用合适的算法和数据结构来实现正确和高效的解决方案。 对于整理ACM-ICPC模板,以下是一些建议: 1. 了解比赛要求:首先,你需要了解ACM-ICPC比赛的具体要求和规则。这包括了解比赛所涉及的算法和数据结构,以及题目的类型和难度等。 2. 收集资料:收集与ACM-ICPC相关的资料,包括经典算法和数据结构的实现代码、常见问题的解题思路等。可以参考教材、博客、论文等资源。 3. 整理模板:将收集到的资料整理成模板。可以按照算法和数据结构的分类进行整理,例如排算法、图算法、字符串算法等。对每个模板,添加必要的注释和示例代码,以便理解和使用。 4. 测试代码:对每个模板编写测试代码,确保它们的正确性和可靠性。可以使用已知的测试用例或自行设计测试用例。 5. 更新与扩充:定期更新和扩充模板,以适应ACM-ICPC比赛中新出现的算法和数据结构。同时,根据自己的经验和理解,对模板进行优化和改进。 6. 练习和复习:在比赛之前,利用整理好的模板进行练习和复习。尝试解决一些经典问题,使用模板中的算法和数据结构进行实现,并进行优化。 希望这些建议对你整理ACM-ICPC模板有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值