[bzoj]2131: 免费的馅饼

原题链接:免费的馅饼

题意

接饼子游戏,饼子每秒下落一格,然后人每秒可以向左或者向右移动一格或者两格,当然也可以原地不动。

问最多接到的饼子价值。

分析

发现网上的做法都是dp+树状数组优化的。

这里提供一个另外的思路。

由于每秒向左向右移动的步数可以是$0,1,2$步,转化在坐标系中可以是一个夹角,右边界的斜率$k=\frac{\sqrt{5}}{5}$。

我们可以旋转坐标系,让$x$轴和右边界对齐,然后再旋转$y$轴,把原坐标系变为一个斜坐标系,这样子我们接完每个饼之后只能向$x,y$的正方向移动。

然后按照横坐标排序之后就变成一个求一个带权$LIS$的问题了。

怎么处理带权$LIS$呢,拆点,每个权值为$v_i$的点拆成$v_i$个点,这样就不带权了,然后求个$LIS$就行了。

但是这里的$n*v_i$是$10^8$还需要一步优化。

我们开一个$double->int$的$std::map$,按照$nlogn$的$LIS$算法,每次插入一个值就查找后继,删除掉跟插入一样多的后继,如果后继不够说明答案增加。

把全部点都插完之后直接遍历$std::map$然后把所有的权值累加就行了

代码

#include <bits/stdc++.h>
#define ll long long
#define Mid ((l+r)>>1)
#define ull unsigned long long
using namespace std;
const double Pi=acos(1.0);
int read(){
    char c;int num,f=1;
    while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    while(c=getchar(), isdigit(c))num=num*10+c-'0';
    return f*num;
}
const int N=1e5+1009;
struct node{double x,y;int w;}a[N];
map<double,int>g; 
map<double,int>::iterator it,it2;
int n,w;
double Sin,Cos,Sin2,Cos2;
bool cmp(node a,node b){return a.x<b.x;}
int main()
{
    Sin=1.0/sqrt(5);
    Cos=2.0/sqrt(5);
    Sin2=2*Cos*Cos-1; 
    Cos2=2*Sin*Cos;
    w=read();n=read();
    for(int i=1;i<=n;i++){
        double y=read(),x=read(),p;
        a[i].w=read();
        a[i].x=Cos*x+Sin*y;a[i].y=Cos*y-Sin*x;
        //先把坐标系旋转到x轴与k=1/sqrt(5)相同 
        x=a[i].x;y=a[i].y;
        a[i].x=x+y*Sin2/Cos2;a[i].y=y/Cos2;
        //再把直角坐标系映射到斜坐标系 
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++){
        int t=a[i].w;
        if(g.find(a[i].y)!=g.end())g[a[i].y]+=t;
        else g[a[i].y]=t;
        it=++g.find(a[i].y);
        while(t&&it!=g.end()){
            if((it->second)<=t){
                it2=it++;
                t-=(it2->second);
                g.erase(it2);
            }else{
                (it->second)-=t;
                t=0;
            } 
        }
        
    }
    int ans=0;
    for(it=g.begin();it!=g.end();it++)
        ans+=(it->second);
    printf("%d\n",ans);
    return 0;
}
/*
建立坐标系,发现每次移动都是45度角
旋转坐标系变为45度角,求最长不下降子序列即可 
虽然带权,但是点权很小,直接把一个点拆成点权个数个点 
  
*/ 
View Code

 

posted @ 2019-08-09 10:24  _onglu  阅读(226)  评论(1编辑  收藏  举报