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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liyizhixl/article/details/80335270

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=1nj=1m(i+j)[gcd(i,j)=1]

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

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

  • 那么则有:

    g(d)=i=1ndf(di)

  • 反演得:

    f(d)=i=1ndg(di)μ(i)

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

  • 第一次等差数列变换:

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

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

  • 第二次等差数列变换:

    g(d)=(d+mdd)ndmd2+(dmd+dndmd)nd2
    g(d)=d(2+nd+md)ndmd2

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

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

    f(d)=i=1ndg(di)μ(i)
    f(d)=i=1nddi(2+ndi+mdi)ndimdi2μ(i)

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

    f(1)==i=1ni(2+ni+mi)nimi2μ(i)

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

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

    pre[i]=j=1ijμ(j)

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

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

  • 总时间复杂度 O(m(Ax+Ay)+kmlog 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;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页