ZOJ 3824Fiber-optic Network 树形dp 2014牡丹江现场赛F

来源:互联网 发布:国家卫生网络统计报表 编辑:程序博客网 时间:2024/05/16 05:59
Fiber-optic Network

Time Limit: 15 Seconds      Memory Limit: 262144 KB

Marjar University has decided to upgrade the infrastructure of school intranet by using fiber-optic technology. There are N buildings in the school. Each building will be installed with one router. These routers are connected by optical cables in such a way that there is exactly one path between any two routers.

Each router should be initialized with an operating frequency Fi before it starts to work. Due to the limitations of hardware and environment, the operating frequency should be an integer number within [LiRi]. In order to reduce the signal noise, the operating frequency of any two adjacent routers should be co-prime.

Edward is the headmaster of Marjar University. He is very interested in the number of different ways to initialize the operating frequency. Please write a program to help him! To make the report simple and neat, you only need to calculate the sum of Fi (modulo 1000000007) in all solutions for each router.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains one integer N (1 <= N <= 50). The next line contains N integers Li (1 <= Li <= 50000). Then, the following line contains N integers Ri (Li <= Ri<= 50000).

For the next N - 1 lines, each line contains two integers Xi and Yi. That means there is an optical cable connecting router Xi and router Yi (indexes are 1-based).

Output

For each test case, output a line with N integers representing the sum of Fi (modulo 1000000007) in all solutions.

Sample Input

241 2 3 42 3 4 51 22 33 441 2 3 42 3 4 51 21 31 4

Sample Output

5 10 14 1910 23 31 41

Hint

In the first sample test case, there are 4 ways to initialize the operating frequency:

  • 1 2 3 4
  • 1 2 3 5
  • 1 3 4 5
  • 2 3 4 5



题意:有一颗树,要为每个节点赋一个值Fi,使得任意相邻的节点互素。然后对每个节点统计Fi在所有可能中的和。


思路:其实题目要求的就是对于每个点我们赋值为X的时候有多少种方案,然后每个点的答案就是 sigma(x * (对应方案数))。好了,我们的注意力转移到如何求方案数。

我们先看下当树是一条链的时候的情况,因为这种情况比较简单。 我们可以容易得到转移方程dp[i][j] = sigma(dp[i-1][k]) (k 与 j互素) 其中dp[i][j]表示前i个节点,第i个节点赋为j时的方案数。 但是显然复杂度已经是 50 * 50000 * 50000 ,太大了,不能忍!!而且我们能注意到其实互素的个数是很多的,我们不妨先求出跟j不是互素的,然后用总的减掉不是互素的方案数。如果是求不是互素要怎么求呢?我们能够用容斥,我的代码里貌似是类似莫比乌斯反演的东西? 我们把j进行分解素因子,然后枚举一下因子的组合容斥,选奇数个因子就是加上,偶数个就是减去(如果不懂我说什么可以先了解一下容斥的思想,并不是简单的全集减去对立面)。

如果要这么做,我们的状态的意义就要改一下了,这时候dp[i][j]中的j表示的是第i个节点的数字能被j整除时的方案数,我们继续注意到对每个赋的值x进行容斥统计的时候,我们只关注它有什么素因子,所以我们能继续把一些素因子完全一样的数一起进行统计。于是我们在节点i枚举每个素因子组成不同的数x来统计方案数S,然后把S加到每个dp[i][k] (k是x因子,因为我们的状态表示的是该位置的数字能被k整除时的方案数)。这样单链的情况我们基本就做出来了,当是树的时候一样的,只不过方案数是各个分支的方案数的乘积而已。

好了,现在如果知识要求出一个点Fi的和很简单是吧?但是题目要求所有的点的Fi和。我们当然可以直接以每个点作为根来做一遍dp,我这样做之间简单粗暴的返回tle。我们能不能把那个n*n的复杂度优化为n呢?是可以的。我们考虑一个题目:在一颗树里,求出每个点的最远节点的距离 。我们可以用dp[u][fa]表示在把(u,fa)这条边砍掉后,以u为根的子树中的“最远距离”,那么我们的递推就是dp[u][fa] = max(dp[v][u] + 1) (v是u的儿子)。 这里我们能够用记忆化搜索来做。 回到这道题,可以用一样的处理方法。不过直接多加一维会爆内存的,其实那个二维的状态我们可以用O(n)的空间存的,因为每一条边对应两个状态,所以状态数只有2*n-2. 我们另外开一个id[maxn][maxn]对状态进行标记就行了。具体的可以看代码。


代码:

#include <iostream>#include <vector>#include <algorithm>#include <string.h>#include <cstring>#include <stdio.h>#include <cmath>#include <math.h>#define rep(i,a,b) for(int i=(a);i<(b);++i)#define rrep(i,b,a) for(int i = (b); i >= (a); --i)#define clr(a,x) memset(a,(x),sizeof(a))#define LL long longusing namespace std;const int maxn = 50 + 5;const int maxv = 50000 + 5;const int mod = 1e9 + 7;LL num[maxn * 2][maxv];LL Count[maxn * maxn];int id[maxn][maxn];vector<int> pe[maxv];vector<int> fac[maxv];  int same[maxn][maxv];int h[maxv];int miu[maxv];int n;int l[maxn], r[maxn];vector<int> kind[maxn];vector<int> adj[maxn];void add(LL & a, LL b){a += b;if (a >= mod) a -= mod;}LL Cal_num(LL * arr, int x, LL amt){rep(i, 0, fac[x].size()) {int z = fac[x][i];amt += arr[z] * miu[z];if (amt >= mod) amt -= mod;if (amt < 0) amt += mod;}return amt;}bool vis[maxn][maxn];void dfs(int u, int fa){    if (vis[u][fa]) return;bool isleaf = true;vis[u][fa] = true;rep(i, 0, adj[u].size()) {int v = adj[u][i];if (v == fa) continue;isleaf = false;dfs(v, u);}if (isleaf) {rep(d, 0, kind[u].size()) {    int i = kind[u][d];rep(j, 0, fac[i].size()) {int x = fac[i][j];num[id[u][fa]][x] += same[u][i];}Count[id[u][fa]] += same[u][i];}}else {//统计方案数LL way;rep(e, 0,kind[u].size()) {int d = kind[u][e];way = same[u][d];rep(i, 0, adj[u].size()) {int v = adj[u][i];if (v == fa) continue;way = (way * Cal_num(num[id[v][u]], d, Count[id[v][u]])) % mod;}rep(j, 0, fac[d].size()) {int x = fac[d][j];add(num[id[u][fa]][x], way);}add(Count[id[u][fa]], way);}}}void solve(){    clr(id,-1);    clr(Count,0); clr(num,0); clr(vis,0);    int c = 0;    rep(i,1,n+1) {        rep(j,0,adj[i].size()) {            id[i][adj[i][j]] = ++c;        }//        id[i][i] = ++c;    }    rep(u,1,n+1) {        rep(j,0,adj[u].size()) {            int v = adj[u][j];            dfs(u,v);        }//        dfs(u,u);    }rep(u, 1, n + 1) {LL ans = 0;rep(i, l[u], r[u] + 1) {LL way = 1;rep(j, 0, adj[u].size()) {int v = adj[u][j];way = (way * Cal_num(num[id[v][u]], i, Count[id[v][u]])) % mod;}add(ans, way * i % mod);}if (u != 1) printf(" ");printf("%lld", ans);}puts("");}void read_int(int & x){    char ch = getchar();    while (ch < '0' || ch > '9') ch = getchar();    x = ch - '0'; ch = getchar();    while ('0' <= ch && ch <= '9') {        x = 10 * x + ch - '0';        ch = getchar();    }}void Getinput(){freopen("in.txt", "w", stdout);int T = 1; printf("%d\n", T);while (T--) {int n = 50; printf("%d\n", n);rep(i, 0, n) printf("1 ");puts("");rep(i, 0, n) printf("50000 ");puts("");rep(i, 0, n - 1) printf("%d %d\n", i + 1, i + 2);}}int main(){//Getinput(); return 0;//freopen("in.txt", "r", stdin);//    freopen("out.txt","w",stdout);rep(i, 1, maxv) {int x = i;if (~x & 1) {pe[i].push_back(2);while (~x & 1) x >>= 1;}for (LL j = 3; j*j <= x; j += 2) if (x % j == 0) {pe[i].push_back(j);do x /= j;while (x % j == 0);}if (x > 1) pe[i].push_back(x);//printf("%u\n", fac[i].size());int k = pe[i].size();int maxx = 1;rep(s, 1, 1 << k) {int x = 1;int odd = 0;rep(j, 0, k) if (s & (1 << j)) x *= pe[i][j], odd ^= 1;if (x > maxx) maxx = x;miu[x] = odd ? -1 : 1;fac[i].push_back(x);}h[i] = maxx;//printf("%u\n", fac[i].size());}int T; cin >> T;while (T--) {        read_int(n);//scanf("%d", &n);rep(i, 1, n + 1) read_int(l[i]); //scanf("%d", l + i);rep(i, 1, n + 1) read_int(r[i]); //scanf("%d", r + i);clr(same, 0);rep(i, 1, n + 1) {kind[i].clear();rep(j, l[i], r[i] + 1) {if (++same[i][h[j]] == 1) kind[i].push_back(h[j]);}//printf("%u\n",kind[i].size());}rep(i, 0, maxn) adj[i].clear();rep(i, 1, n) {int u, v; read_int(u); read_int(v); //scanf("%d%d", &u, &v);adj[u].push_back(v);adj[v].push_back(u);}solve();}}


0 0
原创粉丝点击