A - Print Article HDU - 3507
题意:一台老旧的打字机,每打出一个单词就会有一个ci.输出一行的费用为 ∑(ci)+M(M是一个常数) 现在要输出一篇文章,问费用最少为多少。
分析:dp[i]:前i个单词输出的最少费用
dp[i]=min(dp[j],dp[j]+(sum[i]−sum[j])2)
时间复杂度为n*n,n的范围是5e5,会T
这个时候就需要优化了,把min的范围变小就好了。
假设k< j < i,j比k优,则可以得到
dp[j]+sum[j]2−(dp[k]+sum[k]2)/(2∗sum[j]−2∗sum[k])<=sum[i]
设
yj=dp[j]+sum[j]2,xj=2∗sum[j]
g(j,k)=yj-yk/(xj-xk),如果g(j,k)<=sum[i],则j比k优,那么k就可以去掉了
如果g(i,j) < g(j,k)
if(g(j,k)<=sum[i]) 表示i比j优,j比k优,j,k可以去掉
if(g(i,j)>sum[i]) 表示j比i优,k比j优,i,j可以去掉
综合之上,if(g(i,j)< g(j,k)) 则j可以去掉
用队列维护,这样的话,我们没去掉的点,也就是队列中的点连起来就是下凸形。
因为g(i,j)相当于斜率,也就是一阶导,当任意g(j,k)<=g(i,j),也就是二阶导大于或者等于0.这个时候,函数呈上凹也就是下凸形
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define ll long long
const int maxn = 5e5+10;
ll dp[maxn],q[maxn],sum[maxn];
ll y(int j,int k)
{
return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];
}
ll x(int j,int k)
{
return 2*(sum[j]-sum[k]);
}
ll getdp(int i,int j)
{
return dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j]);
}
int main()
{
ll n,m;
while(scanf("%I64d %I64d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%I64d",&sum[i]);
sum[0]=dp[0]=0;
for(int i=1;i<=n;i++)
sum[i]+=sum[i-1];
int head=0,tail=0;
q[tail++]=0;
for(int i=1;i<=n;i++)
{
while(head+1<tail&&y(q[head+1],q[head])<=sum[i]*x(q[head+1],q[head]))
head++;
dp[i]=getdp(i,q[head])+m;
while(head+1<tail&&y(i,q[tail-1])*x(q[tail-1],q[tail-2])<=y(q[tail-1],q[tail-2])*x(i,q[tail-1]))
tail--;
q[tail++]=i;
}
printf("%I64d\n",dp[n]);
}
return 0;
}
ps:做题做着做着,忽然想队列优化while判断中,能不能直接用getdp()来表示呢,那样就不用化简了,这样
while(head+1<tail&&getdp(i,q[head+1])<=getdp(i,q[head]))
head++;
dp[i]=getdp(i,q[head])+m;
while(head+1<tail&&getdp(i,q[tail-2])*getdp(i,i)<=getdp(i,q[tail-1])*getdp(i,q[tail-1]))
tail--;
第一个while还可以改,但是第二个就不行了呀,因为g(j,k)和i并没有关系,不能直接用i。
2016年沈阳区域赛I 题 The Elder UVALive - 7620
题意:给你一棵树,节点从1到n,代表n个城市。1是首都,现在要从每个城市分别送信到首都。邮差可以在中途经过的城市选择继续送信或者给别的邮差送,继续送信的时间是 (lena+lenb)2 ,让别的邮差送的话,中间需要p时间来交接,时间花费是 lena2+lenb2+p ,每个城市送信到首都的花费要最小,这些花费中最大的是?
分析:就是斜率dp嘛
状态转移:
dp[i]=min(dp[j]+(sum[i]−sum[j])2+p);
这里有一点不同,就是这个斜率dp是在树上的,要注意还原。。。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <stack>
//#pragma comment(linker,"/STACK:102400000,102400000")
#define IN freopen("D:\\in.txt","r",stdin)
#define OUT freopen("D:\\out.txt","w",stdout)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 2147493647
using namespace std;
const int maxn = 1e5+10;
int n;ll p;
struct point
{
int pos,time,z;
};
stack<point> st;
struct node
{
int pre,to;ll w;
}e[maxn*2];
int head[maxn],vis[maxn],q[maxn];
ll dp[maxn],sum[maxn];
int head1=0,tail=0;
void init()
{
mem(head,-1);mem(vis,0);mem(e,0);mem(sum,0);mem(dp,0);
}
void addedge(int h,int from,int to,ll w)
{
e[h].to=to;e[h].pre=head[from];e[h].w=w;head[from]=h;
}
ll y(int j,int k)
{
return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];
}
ll x(int j,int k)
{
return 2ll*(sum[j]-sum[k]);
}
ll getdp(int i,int j)
{
return dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+p;
}
int dfs_num=0;
void dfs(int u)
{
int ti=++dfs_num;
while(head1+1<tail&&y(q[head1+1],q[head1])<=sum[u]*x(q[head1+1],q[head1]))
head1++;
dp[u]=getdp(u,q[head1]);
while(head1+1<tail&&y(u,q[tail-1])*x(u,q[tail-2])<=y(u,q[tail-2])*x(u,q[tail-1])){
tail--;
point a;
a.pos=tail;
a.time=dfs_num;
a.z=q[tail];
st.push(a);
}
q[tail++]=u;
int nowhead1=head1,nowtail=tail;
for(int i=head[u];i>-1;i=e[i].pre)
{
if(vis[e[i].to]) continue;
vis[e[i].to]=1;
head1=nowhead1,tail=nowtail;
while(!st.empty())
{
point t=st.top();
if(t.time<=ti) break;
q[t.pos]=t.z;
st.pop();
}
sum[e[i].to]=sum[u]+e[i].w;
dfs(e[i].to);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
int h=0;
scanf("%d %lld",&n,&p);
for(int i=0;i<n-1;i++)
{
int from,to;ll w;
scanf("%d %d %lld",&from,&to,&w);
addedge(h,from,to,w);h++;
addedge(h,to,from,w);h++;
}
vis[1]=1;
while(!st.empty()) st.pop();
head1=0,tail=0;
dfs_num=0;
dp[1]=-p;
for(int i=head[1];i>-1;i=e[i].pre)
{
if(vis[e[i].to]) continue;
vis[e[i].to]=1;
sum[e[i].to]=sum[1]+e[i].w;
while(!st.empty()) st.pop();
head1=0,tail=0;
q[tail++]=1;
dfs(e[i].to);
}
ll ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
cout<<ans<<endl;
}
return 0;
}