Description
Input
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
Hint
Solution
这题的瓶颈就在于如何快速地求出边权的大小。
求出来后就可以直接二分答案+spfa了。
考虑莫比乌斯反演(设边两端的权值分别为 ):
设函数
显然我们要求的答案即为 ,但是直接求 并不容易,于是我们再设函数
那么则有:
反演得:
于是 可以直接求,用两次等差数列求和来表示:(首项+末项)*项数/2。
第一次等差数列变换:
(原理就是 的贡献和 的贡献分开算, 的贡献满足等差数列的形式)
第二次等差数列变换:
原理也是一样,带 的项满足等差数列的形式,而后面的常数项先提出来。
于是我们可以表示出 ,即:
那我们要求的 就能长成这样:
对于 , 这样的形式我们都可以分块解决(值最多只有 种)。
同时维护一个前缀和 即可:
这样分块计算时就可以把值相同的一段段算了。
于是我们就快速地求出了边权,剩下的二分+spfa就很简单了。
总时间复杂度 。
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;
}