码迷,mamicode.com
首页 > 其他好文 > 详细

dtoi1363 楼房重建 (rebuild)

时间:2020-01-29 00:54:18      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:||   最长上升子序列   +=   mes   i++   sum   col   cst   这一   

题意:

     有n个位置上可以盖房子,一开始每个位置都是空的,每次会让你将位置x的房子的高度变成y,询问从0可以看到多少栋房子,x,y给定。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。

题解:

     如果将一个房子的最高点和(0,0)点的连线的斜率作为每个点的权值,那么答案就是贪心上升子序列的长度。贪心上升子序列定义为:一开始队列为空,每次从1开始,遇到一个值比队尾大就把它加入队尾。(与最长上升子序列不同)

     此题可以使用线段树,然而我并不知道怎么做,所以我使用了分块。

     对于每一块中单独求一次贪心上升子序列。然后求答案的时候,对于每一块,只需要把当前队列里最后一个元素取出来,在当前块的贪心上升子序列中找到第一个比这个元素大的位置,就可以计算这一块的贡献了。不过需要二分查找,效率为O(n*块数*log(n))。(当我写到这里时,我意识到并不需要二分,可以去掉一个log,但我懒得改了)。不过这效率实在是低,所以多取几个块大小试几次就可以过了。

#include<cstdio>
#include<cmath> 
#include<algorithm>
#include<cstdlib>
using namespace std;
int n,m,fk,cnt,g[3002][3002],len[10000];
double h[100002];
int ccj(int num,int x,int y,int t){
    while(x<y)
    {
        int mid=(x+y)/2;
        if (h[g[num][mid]]/g[num][mid]>h[t]/t)y=mid;else x=mid+1;
    }
    return x;
}
int getans(){
    int x=1,ans=1;
    for (int i=0;i<=cnt;i++)
    {
        int wz=ccj(i,0,len[i],x),sum;
        sum=len[i]-wz;
        if (sum)
        {
            x=g[i][len[i]-1];ans+=sum;
        }
    }
    if (!h[1])ans--;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    fk=600;
    for (int i=1;i<=n;i++)
    {
        cnt=i/fk;
        if (!len[i/fk])g[i/fk][len[i/fk]++]=i;
    }
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        h[x]=y;len[x/fk]=0;int t=x/fk;
        for (int i=max(x/fk*fk,1);i<(x/fk+1)*fk&&i<=n;i++)
        if (!len[t] || h[i]/i>h[g[t][len[t]-1]]/g[t][len[t]-1])g[t][len[t]++]=i;
        printf("%d\n",getans());
    }
    return 0;
}

dtoi1363 楼房重建 (rebuild)

标签:||   最长上升子序列   +=   mes   i++   sum   col   cst   这一   

原文地址:https://www.cnblogs.com/1124828077ccj/p/12239337.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!