正题
题目大意
nnn个点,每个点有一个wiw_iwi,mmm条边,对于一条边(x,y)(x,y)(x,y),边权为∑i=1wx∑j=1wy[gcd(i,j)==1](i+j)\sum_{i=1}^{w_x}\sum_{j=1}^{w_y}[gcd(i,j)==1](i+j)i=1∑wxj=1∑wy[gcd(i,j)==1](i+j)
选择一个最小的PPP使得所有边权减去PPP(不能小于000)使得最短路长度不超过TTT
解题思路
一道缝合题,边权要莫反算
f(x)=∑i=1n∑j=1m[gcd(i,j)==x](i+j)f(x)=\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==x](i+j)f(x)=i=1∑nj=1∑m[gcd(i,j)==x](i+j)
那么有F(x)=∑x∣df(d)=∑i=1⌊nx⌋∑i=1⌊mx⌋(i∗⌊nx⌋+j∗⌊mx⌋)F(x)=\sum_{x|d}f(d)=\sum_{i=1}^{\lfloor\frac{n}{x}\rfloor}\sum_{i=1}^{\lfloor\frac{m}{x}\rfloor}(i*\lfloor\frac{n}{x}\rfloor+j*\lfloor\frac{m}{x}\rfloor)F(x)=x∣d∑f(d)=i=1∑⌊xn⌋i=1∑⌊xm⌋(i∗⌊xn⌋+j∗⌊xm⌋)
然后有f(x)=∑x∣dF(dx)μ(d)f(x)=\sum_{x|d}F(\frac{d}{x})\mu(d)f(x)=x∣d∑F(xd)μ(d)
f(1)=∑i=1nF(i)μ(i)f(1)=\sum_{i=1}^nF(i)\mu(i)f(1)=i=1∑nF(i)μ(i)
然后前面那个F(x)F(x)F(x)整除分块+等差数列求和,前面那个μ\muμ求前缀和算即可。
然后后面直接二分一下P+dijP+dijP+dij就好了。
时间复杂度O(mwi+(n+m)logn)O(m\sqrt w_i+(n+m)\log n)O(mwi+(n+m)logn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll N=1e4+10,M=1e5+10;
struct node{ll to,next,w;
}a[N*4];
struct point{ll x,w;
};
bool operator<(point x,point y)
{return x.w>y.w;}
priority_queue<point> q;
ll n,m,T,tot,w[N],ls[N],f[N];
ll mu[M],pri[M],cnt;
bool v[N],vis[M];
void prime(){mu[1]=1;for(ll i=2;i<M;i++){if(!vis[i])pri[++cnt]=i,mu[i]=-1;for(ll j=1;j<=cnt&&pri[j]*i<M;j++){vis[pri[j]*i]=1;if(i%pri[j]==0)break;mu[pri[j]*i]=-mu[i];}}for(ll i=1;i<M;i++)mu[i]=mu[i]*i+mu[i-1];return;
}
ll s(ll n){return n*(n+1)/2;}
ll solve(ll n,ll m){ll ans=0;if(n>m)swap(n,m);for(ll l=1,r;l<=n;l=r+1){r=min(n/(n/l),m/(m/l));ans+=(s(n/l)*(m/l)+s(m/l)*(n/l))*(mu[r]-mu[l-1]);}return ans;
}
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
bool check(ll p){memset(f,0x3f,sizeof(f));memset(v,0,sizeof(v));f[1]=0;q.push((point){1,0});while(!q.empty()){ll x=q.top().x,w=q.top().w;q.pop();if(v[x])continue;v[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to,zw=max(a[i].w-p,0ll);if(f[y]>f[x]+zw){f[y]=f[x]+zw;if(!v[y])q.push((point){y,f[y]});}}}return f[n]<=T;
}
signed main()
{freopen("magic.in","r",stdin);freopen("magic.out","w",stdout);prime();scanf("%lld%lld%lld",&n,&m,&T);for(ll i=1;i<=n;i++)scanf("%lld",&w[i]);for(ll i=1;i<=m;i++){ll x,y,val;scanf("%lld%lld",&x,&y);val=solve(w[x],w[y]);addl(x,y,val);addl(y,x,val);}ll l=0,r=1e18;while(l<=r){ll mid=(l+r)>>1;if(check(mid))r=mid-1;else l=mid+1;}check(l);printf("%lld %lld",l,f[n]);return 0;
}