JZOJ 5701. 【gdoi2018 day2】第一题 谈笑风生(magic)

Description

Description

Input

Input

Output

Output

Sample Input

5 7 66
4 3 2 1 5
1 2
1 5
2 3
2 4
2 5
3 4
3 5

Sample Output

6 64

Data Constraint

Data Constraint

Hint

Hint

Solution

  • 这题的瓶颈就在于如何快速地求出边权的大小。

  • 求出来后就可以直接二分答案+spfa了。

  • 考虑莫比乌斯反演(设边两端的权值分别为 n , m   ( n < m ) ):

  • 设函数

    f ( d ) = i = 1 n j = 1 m ( i + j ) [ g c d ( i , j ) = 1 ]

  • 显然我们要求的答案即为 f [ 1 ] ,但是直接求 f 并不容易,于是我们再设函数

    g ( d ) = i = 1 n j = 1 m ( i + j ) [ d | g c d ( i , j ) ]

  • 那么则有:

    g ( d ) = i = 1 n d f ( d i )

  • 反演得:

    f ( d ) = i = 1 n d g ( d i ) μ ( i )

  • 于是 g 可以直接求,用两次等差数列求和来表示:(首项+末项)*项数/2。

  • 第一次等差数列变换:

    g ( d ) = i = 1 n d d i m d + ( d + m d d ) m d 2

  • (原理就是 i 的贡献和 j 的贡献分开算, j 的贡献满足等差数列的形式)

  • 第二次等差数列变换:

    g ( d ) = ( d + m d d ) n d m d 2 + ( d m d + d n d m d ) n d 2
    g ( d ) = d ( 2 + n d + m d ) n d m d 2

  • 原理也是一样,带 i 的项满足等差数列的形式,而后面的常数项先提出来。

  • 于是我们可以表示出 f ( d ) ,即:

    f ( d ) = i = 1 n d g ( d i ) μ ( i )
    f ( d ) = i = 1 n d d i ( 2 + n d i + m d i ) n d i m d i 2 μ ( i )

  • 那我们要求的 f ( 1 )   ( d = 1 ) 就能长成这样:

    f ( 1 ) == i = 1 n i ( 2 + n i + m i ) n i m i 2 μ ( i )

  • 对于 n i m i 这样的形式我们都可以分块解决(值最多只有 O ( n + m ) 种)。

  • 同时维护一个前缀和 p r e [ i ] 即可:

    p r e [ i ] = j = 1 i j μ ( j )

  • 这样分块计算时就可以把值相同的一段段算了。

  • 于是我们就快速地求出了边权,剩下的二分+spfa就很简单了。

  • 总时间复杂度 O ( m ( A x + A y ) + k m l o g   T )

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e4+5;
int n,m,tot;
LL T,ans;
int first[N],nex[N<<2],en[N<<2];
LL w[N<<2];
int a[N],q[N*10],f[N],miu[N],pre[N];
bool bz[N];
LL dis[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void insert(int x,int y,LL z)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline bool check(LL val)
{
    memset(dis,60,sizeof(dis));
    memset(bz,false,sizeof(bz));
    int l=dis[1]=0,r=q[1]=1;
    while(l<r)
    {
        if(dis[n]<=T) return true;
        int x=q[++l];
        bz[x]=false;
        for(int i=first[x];i;i=nex[i])
        {
            LL vv=w[i]-val<0?0:w[i]-val;
            if(dis[x]+vv<dis[en[i]])
            {
                dis[en[i]]=dis[x]+vv;
                if(!bz[en[i]]) bz[q[++r]=en[i]]=true;
            }
        }
    }
    return dis[n]<=T;
}
inline bool getans(LL val)
{
    memset(dis,60,sizeof(dis));
    memset(bz,false,sizeof(bz));
    int l=dis[1]=0,r=q[1]=1;
    while(l<r)
    {
        int x=q[++l];
        bz[x]=false;
        for(int i=first[x];i;i=nex[i])
        {
            LL vv=w[i]-val<0?0:w[i]-val;
            if(dis[x]+vv<dis[en[i]])
            {
                dis[en[i]]=dis[x]+vv;
                if(!bz[en[i]]) bz[q[++r]=en[i]]=true;
            }
        }
    }
}
int main()
{
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    n=read(),m=read();
    scanf("%lld",&T);
    for(int i=1;i<=n;i++) a[i]=read();
    miu[1]=pre[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!bz[i])
        {
            f[++f[0]]=i;
            miu[i]=-1;
        }
        for(int j=1;j<=f[0] && i*f[j]<N;j++)
        {
            bz[i*f[j]]=true;
            if(i%f[j]==0)
            {
                miu[i*f[j]]=0;
                break;
            }
            miu[i*f[j]]=-miu[i];
        }
        pre[i]=pre[i-1]+miu[i]*i;
    }
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        int vx=a[x],vy=a[y];
        if(vx>vy) swap(vx,vy);
        LL sum=0;
        for(int j=1,p;j<=vx;j=p+1)
        {
            p=min(vx/(vx/j),vy/(vy/j));
            int xx=vx/p,yy=vy/p;
            sum+=(LL)xx*yy*(xx+yy+2)/2*(pre[p]-pre[j-1]);
        }
        insert(x,y,sum);
        insert(y,x,sum);
    }
    LL l=0,r=1e18;
    while(l<=r)
    {
        LL mid=l+r>>1;
        if(check(mid))
        {
            ans=mid;
            r=mid-1;
        }else l=mid+1;
    }
    getans(ans);
    printf("%lld %lld",ans,dis[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/liyizhixl/article/details/80335270
今日推荐