ZOJ3824 Fiber-optic Network

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
2
4
1 2 3 4
2 3 4 5
1 2
2 3
3 4
4
1 2 3 4
2 3 4 5
1 2
1 3
1 4
Sample Output
5 10 14 19
10 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


Author:  JIANG, Kai

Source: The 2014 ACM-ICPC Asia Mudanjiang Regional Contest


这是一道很让人印象深刻的题目0.0

写的也非常的丑(dfs了3次,用了好多个vector)

憋了一下午,还被喷了为何偷懒没写题(QAQ)

但是感觉非常锻炼能力。


先给出完整代码。

#include<stdio.h>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>

#define msn(x,y) (memset((x),0,sizeof((x[0]))*(y+3)))
#define msx(x,y) (memset((x),0x7f,sizeof((x[0]))*(y+3)))
#define fuck(x) cerr << #x << " <- " << x << endl
#define acer cout<<"sb"<<endl
typedef long long ll;
using namespace std;

int sumpr;
int pr[50001];
bool flag[50001];
vector<int>p[50001];

void get_prime()
{
    for(int i=2; i<=50001; i++)
    {
        if(!flag[i])
        {
            pr[sumpr++]=i;
            for(int j=2*i; j<=50001; j+=i)flag[j]=1;
        }
    }
    for(int i=1; i<=50000; i++)
    {
        int cmp=i;
        for(int j=0; cmp!=1&&j<sumpr; j++)
        {
            if(cmp%pr[j]==0)
            {
                p[i].push_back(pr[j]);
                while(cmp%pr[j]==0)cmp/=pr[j];
            }
        }
    }
}
const ll mod=1e9+7;
ll extend_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=extend_gcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return ans;
}
ll inverse(ll b)
{
    ll x,y;
    extend_gcd(b,mod,x,y);
    return (x%mod+mod)%mod;
}

const int maxn=55;
vector<int>g[maxn];
vector<int>gg[maxn];

int l[maxn],r[maxn];
int n,m;
ll dp[55][50001];
ll di[55][50001];
ll sum[55];
ll ans[55];
vector<ll>cmp[55][50001];
void dfs0(int u,int fa)
{
    for(int i=0; i<gg[u].size(); i++)
    {
        int v=gg[u][i];
        if(v!=fa)
        {
            g[u].push_back(v);
            dfs0(v,u);
        }
    }
}
void dfs(int u,int fa)
{
    for(int i=l[u]; i<=r[u]; i++)dp[u][i]=1;
    for(int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i];
        dfs(v,u);
    }
    for(int j=l[u]; j<=r[u]; j++)
    {
        cmp[u][j].clear();
        for(int i=0; i<g[u].size(); i++)
        {
            int v=g[u][i];
            int sz=p[j].size();
            ll s=0;
            for(int k=1; k<(1<<sz); k++)
            {
                ll a,b;
                a=-1;
                b=1;
                for(int l=0; l<sz; l++)
                {
                    if(k&(1<<l))
                    {
                        b=b*p[j][l];
                        if(b>50000)b=0;
                        a=a*(-1);
                    }
                }
                if(b>0)s=(s+a*di[v][b]%mod)%mod;
            }
            dp[u][j]=(dp[u][j]*(((sum[v]-s)%mod+mod)%mod))%mod;
            cmp[u][j].push_back((((sum[v]-s)%mod+mod)%mod));
        }
    }
    sum[u]=0;
    for(int i=1; i<=50000; i++)
    {
        sum[u]+=dp[u][i];
        sum[u]%=mod;
        di[u][i]=0;
        for(int j=1; j*i<=50000; j++)
        {
            di[u][i]=(di[u][i]+dp[u][j*i])%mod;
        }
    }
}


void dfs2(int u,int fa)
{
    for(int i=l[u];i<=r[u];i++)ans[u]=(ans[u]+dp[u][i]*i%mod)%mod;

    for(int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i];
        for(int j=l[u]; j<=r[u]; j++)
        {
            if(dp[u][j]!=0)
            {
                ll ni=inverse(cmp[u][j][i]);
                dp[u][j]=ni*dp[u][j]%mod;
            }
        }
        sum[u]=0;
        for(int ii=1; ii<=50000; ii++)
        {
            di[u][ii]=0;
            sum[u]+=dp[u][ii];
            for(int j=1; j*ii<=50000; j++)
            {
                di[u][ii]=(di[u][ii]+dp[u][j*ii])%mod;
            }
        }
        for(int j=l[v]; j<=r[v]; j++)
        {
            int sz=p[j].size();
            ll s=0;
            for(int k=1; k<(1<<sz); k++)
            {
                ll a,b;
                a=-1;
                b=1;
                for(int l=0; l<sz; l++)
                {
                    if(k&(1<<l))
                    {
                        b=b*p[j][l];
                        if(b>50000)b=0;
                        a=a*(-1);
                    }
                }
                if(b>0)s=(s+a*di[u][b]%mod)%mod;
            }
             dp[v][j]=(dp[v][j]*(((sum[u]-s)%mod+mod)%mod))%mod;
        }
        dfs2(v,u);
        for(int j=l[u]; j<=r[u]; j++)
        {
            if(dp[u][j]!=0)
            {
                dp[u][j]=cmp[u][j][i]*dp[u][j]%mod;
            }
        }
    }
}
int main()
{
    int T;
    get_prime();
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d",&n);
    memset(ans,0,sizeof(ans));
     memset(sum,0,sizeof(sum));
     memset(dp,0,sizeof(dp));
     memset(di,0,sizeof(di));
    for(int i=1; i<=n; i++)g[i].clear(),gg[i].clear();
    for(int i=1; i<=n; i++)scanf("%d",&l[i]);
    for(int i=1; i<=n; i++)scanf("%d",&r[i]);
    int u,v;
    for(int i=0; i<n-1; i++)
    {
        scanf("%d%d",&u,&v);
        gg[u].push_back(v);
        gg[v].push_back(u);
    }
    dfs0(1,0);
    dfs(1,0);
    dfs2(1,0);
    for(int i=1;i<=n;i++)
    {
        if(i!=1)printf(" ");
        printf("%lld",ans[i]);
    }
    printf("\n");
    }
    return 0;
}

题意中,节点的个数是50个,数字的范围是50000。

总体思路:

预处理dfs0是将无根树转化为有根树,方便后续两次dfs的处理。

树上的dp嘛,有n-1条边,由于是无向边,在题目中的计算的时候是需要方向的(谁当谁的子树),那么实际上题目里就是要计算2*(n-1)条边,实际上就是每条边给两个方向。

第一次dfs,从1号点开始走,可以求出n-1条有向边的答案。

然后再通过再一次dfs2,求出剩下另外n-1条有向边的答案。

边的答案都求出来了,那么每个节点的每个取值的个数就是相应边的答案之积了。

.

细节思路:

先讲核心:

考虑这样一个孙子问题,给定一个整数区间[L,R](R<=50000),已知区间内的每个数都有一定的数量,每次给出一个查询整数a,现在求区间内与a互素的数的个数。(比如a=9,给定区间[2,4],告诉你2有5个(num[2]=5),3有10个(num[3]=10),4有100个(num[4]=100),那么答案就是5+100=105个)

方法可以用容斥原理做,首先素因子的个数只有几个(假设b个)。那么我们就可以通过预处理之后用容斥原理:2^b的复杂度去解决。

数组di[i]表示

di[i]数组可以通过一个for循环在接近nlogn的复杂度内求出。

然后 

1:指的是i由数个j的素因子累乘而成的。

2:指的是i由数个j的素因子累乘而成的。

s[j]表示取j值的时候与区间不互素的个数。那么和j互素的个数=数字的总数-s[j]

然后这个问题解决了,窝们可以在第一次dfs的时候求出1号节点(根节点的答案)。

然后问题是如何再以较低的时间复杂度更新其他点?

再dfs2一次即可。这里我用的方法有求逆元。。虽然看上去并不是一个十分美的方法,目前也只会这种方法。

假定u的答案已经求好,对于u每个的v节点,窝们只需要除掉(指的是除法(这里通过求逆元))u->v的那条边的方案数,就可以逆转父子关系,再重新预处理求一下di数组和sum数组等等,就能算出v的答案了,以此类推。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值