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

63 篇文章 0 订阅
31 篇文章 0 订阅

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) n , m   ( n < m ) ):

  • 设函数

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

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

    g(d)=i=1nj=1m(i+j)[d|gcd(i,j)]

  • 那么则有:

    g(d)=i=1ndf(di) g ( d ) = ∑ i = 1 ⌊ n d ⌋ f ( d i )

  • 反演得:

    f(d)=i=1ndg(di)μ(i) f ( d ) = ∑ i = 1 ⌊ n d ⌋ g ( d i ) ∗ μ ( i )

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

  • 第一次等差数列变换:

    g(d)=i=1nddimd+(d+mdd)md2

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

  • 第二次等差数列变换:

    g(d)=(d+mdd)ndmd2+(dmd+dndmd)nd2

    g(d)=d(2+nd+md)ndmd2 g ( d ) = d ∗ ( 2 + ⌊ n d ⌋ + ⌊ m d ⌋ ) ∗ ⌊ n d ⌋ ∗ ⌊ m d ⌋ 2

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

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

    f(d)=i=1ndg(di)μ(i) f ( d ) = ∑ i = 1 ⌊ n d ⌋ g ( d i ) ∗ μ ( i )
    f(d)=i=1nddi(2+ndi+mdi)ndimdi2μ(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 )   ( d = 1 ) 就能长成这样:

    f(1)==i=1ni(2+ni+mi)nimi2μ(i) f ( 1 ) == ∑ i = 1 n i ∗ ( 2 + ⌊ n i ⌋ + ⌊ m i ⌋ ) ∗ ⌊ n i ⌋ ∗ ⌊ m i ⌋ 2 ∗ μ ( i )

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

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

    pre[i]=j=1ijμ(j) p r e [ i ] = ∑ j = 1 i j ∗ μ ( j )

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

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

  • 总时间复杂度 O(m(Ax+Ay)+kmlog T) 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值