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

Posted llllrj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
", 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

 

 

以上是关于2017 ACM-ICPC, Universidad Nacional de Colombia Programming Contest K - Random Numbers (dfs序 线段树+数论)的主要内容,如果未能解决你的问题,请参考以下文章

ACM-ICPC 2017 Asia Urumqi(第八场)

2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛

2017 ACM-ICPC 北京网络赛 Minimum 线段树

ACM-ICPC 2017 Asia Urumqi A. Coins

ACM-ICPC 2017 Asia Urumqi G. The Mountain

2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 B题