【XSY1476】平凡之路 斜率优化DP

题目大意

  有\(n\)个格子,一开始你在\(1\)号格子。每次你只能往编号更大的格子走。从第\(i\)个格子走到第\(j\)个格子的代价是\(a_i+a_j\times(j-i)\times m\)

  \(a_i\)为与\(i\)互质且不大于\(i\)的正整数的个数。

  \(n\leq 1000000\)

题解

  显然\(a_i=\varphi(i)\)

  如果\(j<k\)\(j\)\(k\)优,那么

\[\begin{align} f_j+a_j+a_i\times (i-j)\times m&<f_k+a_k+a_i\times (i-k)\times m\\ f_j+a_j+a_iim-a_ijm&<f_k+a_k+a_iim-a_ikm\\ f_j+a_j-a_ijm&<f_k+a_k-a_ikm\\ f_j+a_j-f_k-a_k&<a_ijm-a_ikm\\ \frac{f_j-f_k+a_j-a_k}{j-k}&>a_im \end{align} \]

  这告诉我们要维护一个斜率递增的栈。

  这道题\(a_i\)不像其他题一样是单调递增的,所以要把下凸壳上所有的点保存下来,每次二分找到最优的点。

  时间复杂度:\(O(n\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
	if(a>b)
		swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
	char str[100];
	sprintf(str,"%s.in",s);
	freopen(str,"r",stdin);
	sprintf(str,"%s.out",s);
	freopen(str,"w",stdout);
#endif
}
int rd()
{
	int s=0,c;
	while((c=getchar())<'0'||c>'9');
	do
	{
		s=s*10+c-'0';
	}
	while((c=getchar())>='0'&&c<='9');
	return s;
}
int upmin(int &a,int b)
{
	if(b<a)
	{
		a=b;
		return 1;
	}
	return 0;
}
int upmax(int &a,int b)
{
	if(b>a)
	{
		a=b;
		return 1;
	}
	return 0;
}
int b[1000010];
int pri[1000010];
int cnt;
int phi[1000010];
ll f[1000010];
//ll g[1000010];
int q[1000010];
//int from[1000010];
ll gety(int x)
{
	return f[x]+phi[x];
}
int n,m;
ll calc(int x,int y)
{
	return f[x]+phi[x]+phi[y]*ll(y-x)*m;
}
int main()
{
	open("pfzr");
	scanf("%d%d",&n,&m);
	int i,j;
	phi[1]=1;
	for(i=2;i<=n;i++)
	{
		if(!b[i])
		{
			pri[++cnt]=i;
			phi[i]=i-1;
		}
		for(j=1;j<=cnt&&i*pri[j]<=n;j++)
		{
			b[i*pri[j]]=1;
			if(i%pri[j]==0)
			{
				phi[i*pri[j]]=phi[i]*pri[j];
				break;
			}
			phi[i*pri[j]]=phi[i]*phi[pri[j]];
		}
	}
	memset(f,0x7f,sizeof f);
//	memset(g,0x7f,sizeof g);
//	g[1]=0;
	f[1]=0;
//	for(i=2;i<=n;i++)
//		for(j=1;j<i;j++)
//			if(g[j]+phi[j]+ll(phi[i])*(i-j)*m<g[i])
//			{
//				g[i]=g[j]+phi[j]+ll(phi[i])*(i-j)*m;
//				from[i]=j;
//			}
	int t=0;
	q[++t]=1;
	for(i=2;i<=n;i++)
	{
		int l=1,r=t;
		while(l<r)
		{
			int mid=(l+r)>>1;
			if(gety(q[mid])-gety(q[mid+1])<ll(q[mid]-q[mid+1])*phi[i]*m)
				r=mid;
			else
				l=mid+1;
		}
		f[i]=f[q[l]]+phi[q[l]]+ll(phi[i])*(i-q[l])*m;
		while(t>=2&&(gety(q[t-1])-gety(q[t]))*(q[t]-i)>(gety(q[t])-gety(i))*(q[t-1]-q[t]))
			t--;
		while(t>=1&&gety(q[t])-gety(i)>0)
			t--;
		q[++t]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}
posted @ 2018-03-06 11:27  ywwyww  阅读(200)  评论(0编辑  收藏  举报