COCI 2011/2012 CONTEST #3

题意:

一个工厂制造产品,有N个流程。第i个流程的时间系数是Ti。有M个产品要制造,第i个产品的容易程度是Fi。一个产品j,在流程i所需时间为TiFj。流程顺序不可颠倒,产品也必须按给定的顺序制作。一旦一个流程完成,就交给下一个流程。此时,下一个流程必须是空闲的,不然就会出错。问完成所有产品需要的时间。

数据满足1 ≤ N ≤ 100000,1 ≤ M ≤ 100000。

(翻译来自卓亮wc ppt)

分析题目,容易想到按顺序考虑每个产品,需要用o(n)时间与前一产品判断是否冲突并取最早时间,总复杂度为o(nm),考虑判断条件设bi为i开始时间,si为ti前缀和,我们是想满足此式:

b[j+1]-bj>=max(fj*s[k+1]-f[j+1]*sk),若想让b[j+1]尽量小,则等于右式即可,现在问题转化为求右式。

观察式子,与叉积很像,将同类式子合并则是(f[j],f[j+1])X(s[k],s[k+1]),由于(s[k],s[k+1])是静态的,所以其实是每次用向量(f[j],f[j+1])询问s点集中的叉积最大点(正叉积中最远点),我们可以预处理上凸壳,在上凸壳二分转折点(斜率最近点),即为距向量(f[j],f[j+1])最远点。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
struct point
{
  long long x,y;
}a[200000];
int n,m,t,k,st[200000];
long long s[200000],f[200000],b[200000],ans;
long long max(long long x,long long y) {return (x>y) ? x : y;}
long long cross(point e,point r) {return (e.x*r.y-e.y*r.x);}
long long search(int x,int y)
{
    int l,r,mid;
    long long sum=0;
    point e,u;
    e.x=x,e.y=y;          //e.x=-x,e.y=-y;    (cross(e,u)>0)
    for (l=k+1,r=t;l<=r;)
    {
      mid=(l+r)>>1;
      u.x=a[st[mid]].x-a[st[mid-1]].x;
      u.y=a[st[mid]].y-a[st[mid-1]].y;
      if (cross(e,u)>0) l=mid+1;else r=mid-1;
    }
    sum=max(max(cross(e,a[st[l-1]]),max(cross(e,a[st[l]]),cross(e,a[st[l+1]]))),sum);
    return sum;
}
int check(int x,int y,int z)
{
  point e,r;
  e.x=a[z].x-a[x].x,e.y=a[z].y-a[x].y;
  r.x=a[y].x-a[x].x,r.y=a[y].y-a[x].y;
  long long pd=cross(e,r);
  if (pd>=0) return 1;
  return 0;  
}
void init()
{
  int i;
  long long sum;
  scanf("%d%d\n",&n,&m);
  for (i=1;i<=n;i++) scanf("%d\n",&s[i]);
  for (i=1;i<=m;i++) scanf("%d\n",&f[i]);
  for (i=1;i<=n;i++) s[i]=s[i-1]+s[i],a[i].x=s[i-1],a[i].y=s[i];
  for (st[t=1]=1,i=2;i<=n;i++) {
    for (;(t>1)&&(check(st[t-1],st[t],i));t--) ;st[++t]=i; }
  for (k=t,i=n-1;i>=1;i--){
    for (;(t>k)&&(check(st[t-1],st[t],i));t--) ;st[++t]=i; }
  for (b[1]=0,i=2;i<=m;i++)
    sum=search(f[i-1],f[i]),b[i]=sum+b[i-1];
  ans=b[m]+f[m]*s[n];
  printf("%I64d\n",ans);
}
int main()
{
  freopen("traka.in","r",stdin);
  freopen("traka.out","w",stdout);
    init();
  return 0;
}





/*    for (l=2,r=k;l<=r;)
    {
      mid=(l+r)>>1;
      r.x=a[st[mid]].x-a[st[mid-1]].x;
      r.y=a[st[mid]].y-a[st[mid-1]].y;
      if (cross(e,r)<0) l=mid+1;else r=mid-1;
    }
    sum=max(cross(e,a[st[l-1]]),max(cross(e,a[st[l]])),cross(e,a[st[l+1]]));*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值