【日记】12.4

12.4日记

CDQ分治

  1. HDU1541:给定一些(a,b),定义(a,b)的等级为满足(a2<=a&&b2<=b)的(a2,b2)的个数。输出等级为0-n-1的星星个数。

二维偏序裸题。第一位排好序,第二维树状数组即可。

注意:树状数组不可以处理下标为0的情况,因此需要先+1。或者就直接离散化。离散化的时候,注意int len=unique(a+1,a+n+1)-a-1;

线段树

  1. HDU4578:区间加减+区间乘+区间修改+区间询问一次二次三次方和。

我tm哭了,debug了一天。既然是取模,所以lazy[id][3]标记在没有值的时候要设置为-1,不是用0判断。

思路:修改最优先,乘其次,加减最后。每次pushdown先判断修改,如果没有修改再进行乘和加减。每次修改值的时候,一次性把123次全都修改。

#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=1e5+20;
int v[4*M][4],lazy[4*M][4],P=1e4+7;
inline void push_up(int id){
    for(int i=1;i<=3;++i)
        v[id][i]=(v[id*2][i]+v[id*2+1][i])%P;
}
inline void operate(int *tar,int a,int b,int len){
    tar[3]=(1LL*tar[3]*a*a*a%P+1LL*tar[2]*3*a*a*b%P+1LL*tar[1]*3*a*b*b%P+1LL*b*b*b*len%P)%P,
    tar[2]=(1LL*tar[2]*a*a%P+1LL*tar[1]*2*a*b%P+1LL*b*b*len%P)%P,
    tar[1]=(tar[1]*a+b*len)%P;
}
inline void push_down(int id,int l,int r){
    if (lazy[id][3]!=-1){
        int c=lazy[id][3];
        lazy[id*2][1]=0,lazy[id*2][2]=1,lazy[id*2][3]=c,
        v[id*2][1]=c*(mid-l+1)%P,
        v[id*2][2]=v[id*2][1]*c%P,
        v[id*2][3]=v[id*2][2]*c%P,
        lazy[id*2+1][1]=0,lazy[id*2+1][2]=1,lazy[id*2+1][3]=c,
        v[id*2+1][1]=c*(r-mid)%P,
        v[id*2+1][2]=v[id*2+1][1]*c%P,
        v[id*2+1][3]=v[id*2+1][2]*c%P;
        lazy[id][1]=0,lazy[id][2]=1,lazy[id][3]=-1;
        return;
    }
    int a=lazy[id][2],b=lazy[id][1];
    if (lazy[id*2][3]!=-1)
        lazy[id*2][3]=(lazy[id*2][3]*a+b)%P;
    else
        lazy[id*2][2]=lazy[id*2][2]*a%P,
        lazy[id*2][1]=(lazy[id*2][1]*a+b)%P;
    operate(v[id*2],a,b,mid-l+1);
    if (lazy[id*2+1][3]!=-1)
        lazy[id*2+1][3]=(lazy[id*2+1][3]*a+b)%P;
    else
        lazy[id*2+1][2]=lazy[id*2+1][2]*a%P,
        lazy[id*2+1][1]=(lazy[id*2+1][1]*a+b)%P;
    operate(v[id*2+1],a,b,r-mid);
    lazy[id][1]=0,lazy[id][2]=1,lazy[id][3]=-1;
}
void build(int id,int l,int r){
    for(int i=1;i<=3;++i)
        v[id][i]=lazy[id][i]=0;
    lazy[id][2]=1,lazy[id][3]=-1;
    if (l==r)
        return;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
}
void operate1(int id,int l,int r,int ql,int qr,int a,int b){
    if (ql<=l&&r<=qr){
        if (lazy[id][3]!=-1)
            lazy[id][3]=(lazy[id][3]*a+b)%P;
        else
            lazy[id][2]=(lazy[id][2]*a)%P,
            lazy[id][1]=(lazy[id][1]*a+b)%P;
        operate(v[id],a,b,r-l+1);
        return;
    }
    push_down(id,l,r);
    if (ql<=mid)
        operate1(id*2,l,mid,ql,qr,a,b);
    if (mid<qr)
        operate1(id*2+1,mid+1,r,ql,qr,a,b);
    push_up(id);
}
void operate3(int id,int l,int r,int ql,int qr,int c){
    if (ql<=l&&r<=qr){
        lazy[id][1]=0,lazy[id][2]=1,lazy[id][3]=c,
        v[id][1]=c*(r-l+1)%P,
        v[id][2]=v[id][1]*c%P,
        v[id][3]=v[id][2]*c%P;
        return;
    }
    push_down(id,l,r);
    if (ql<=mid)
        operate3(id*2,l,mid,ql,qr,c);
    if (mid<qr)
        operate3(id*2+1,mid+1,r,ql,qr,c);
    push_up(id);
}
int query(int id,int l,int r,int ql,int qr,int q){
    if (ql<=l&&r<=qr)
        return v[id][q];
    push_down(id,l,r);
    int ans=0;
    if (ql<=mid)
        ans+=query(id*2,l,mid,ql,qr,q);
    if (mid<qr)
        ans+=query(id*2+1,mid+1,r,ql,qr,q);
    return ans%P;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    while(n||m){
        build(1,1,n);
        for(int i=1;i<=m;++i){
            int op,x,y,c;
            scanf("%d%d%d%d",&op,&x,&y,&c);
            switch(op){
                case 1:{
                    operate1(1,1,n,x,y,1,c);
                    break;
                }
                case 2:{
                    operate1(1,1,n,x,y,c,0);
                    break;
                }
                case 3:{
                    operate3(1,1,n,x,y,c);
                    break;
                }
                case 4:{
                    printf("%d\n",query(1,1,n,x,y,c));
                    break;
                }
            }
        }
        scanf("%d%d",&n,&m);
    }
    return 0;
}

单调数据结构

  1. POJ2559:给N个宽度为1的并列的矩形,求选出一个矩形,面积最大。

思路:可以贪心,以第i个矩形为最大模板,能选取的最大面积是左边第一个比他小的位置和右边第一个比他小的位置的距离*h[i],先单调栈求出两边第一个比他小的数,最后再扫一遍,都是\(O(n)\)的。

但对于这种区间最小值*区间长度的最大值的题目,可以直接单调栈处理。

#include<cstdio>
#include<utility>
#include<stack>
using namespace std;
const int M=1e5+20;
int h[M];
#define pii pair<int,int>
#define LL long long
LL rect_max_U(int n,int *h){
    h[n+1]=-1;
    LL ans=0;
    stack<int> st;
    for(int i=1;i<=n+1;++i)
        if (st.empty()||h[i]>h[st.top()])
            st.push(i);
        else{
            int lef;
            while(!st.empty()&&h[i]<=h[st.top()]){
                lef=st.top(),st.pop();
                ans=max(ans,1LL*h[lef]*(i-lef));
            }
            st.push(lef),h[lef]=h[i];
        }
    return ans;
}
int main(){
    int n;
    while(~scanf("%d",&n)&&n){
        for(int i=1;i<=n;++i)
            scanf("%d",&h[i]);
        printf("%lld\n",rect_max_U(n,h));
    }
    return 0;
}
  1. POJ3494:给一个01矩阵,求最大子矩阵的面积。

思路:太妙了,扫每一个横行,实际上相当于做了n遍最大矩形并面积。

#include<cstdio>
#include<utility>
#include<stack>
using namespace std;
const int M=2e3+20;
int mat[M][M],lmin[M],rmin[M];
#define pii pair<int,int>
int rect_max_U(int n,int *h){
    h[n+1]=-1;
    int ans=0;
    stack<int> st;
    for(int i=1;i<=n+1;++i)
        if (st.empty()||h[i]>h[st.top()])
            st.push(i);
        else{
            int lef;
            while(!st.empty()&&h[i]<=h[st.top()]){
                lef=st.top(),st.pop();
                ans=max(ans,h[lef]*(i-lef));
            }
            st.push(lef),h[lef]=h[i];
        }
    return ans;
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j){
                scanf("%d",&mat[i][j]);
                if (mat[i][j])
                    mat[i][j]+=mat[i-1][j];
            }
        int ans=0;
        for(int i=1;i<=n;++i)
            ans=max(ans,rect_max_U(m,mat[i]));
        printf("%d\n",ans);
    }
    return 0;
}
  1. POJ2796:求区间最小值*区间和的最大值。

思路:和POJ2559一样,只不过这里改成了区间和,求前缀和套单调栈即可。本质上是求出让每个值为最小值的最大区间。

#include<cstdio>
#include<utility>
#include<stack>
using namespace std;
const int M=1e5+20;
int h[M],l,r;
#define pii pair<int,int>
#define LL long long
LL sum[M];
LL rect_max_U(int n,int *h){
    h[n+1]=-1;
    LL ans=0;
    stack<int> st;
    for(int i=1;i<=n+1;++i)
        if (st.empty()||h[i]>h[st.top()])
            st.push(i);
        else{
            int lef;
            while(!st.empty()&&h[i]<=h[st.top()]){
                lef=st.top(),st.pop();
                LL c=(sum[i-1]-sum[lef-1])*h[lef];
                if (ans<c)
                    ans=c,l=lef,r=i-1;
            }
            st.push(lef),h[lef]=h[i];
        }
    return ans;
}
int main(){
    int n;
    while(~scanf("%d",&n)&&n){
        l=1,r=n;
        for(int i=1;i<=n;++i)
            scanf("%d",&h[i]),sum[i]=sum[i-1]+h[i];
        LL ans=rect_max_U(n,h);
        printf("%lld\n%d %d\n",ans,l,r);
    }
    return 0;
}

总结

今天调那个线段树调了1w年……服了,以后就长了个教训,不是所有时候都可以用0判断是否存在的,尤其是对于取模的情况。

明日计划

明天比较忙,线段树往前赶一赶,CDQ把二维偏序和三维偏序的裸题写了。

原文地址:https://www.cnblogs.com/diorvh/p/11986681.html

时间: 2024-11-07 21:51:56

【日记】12.4的相关文章

小白日记12:kali渗透测试之服务扫描(二)-SMB扫描

SMB扫描 Server Message Block 协议.与其他标准的TCP/IP协议不同,SMB协议是一种复杂的协议,因为随着Windows计算机的开发,越来越多的功能被加入到协议中去了,很难区分哪些概念和功能应该属于Windows操作系统本身,哪些概念应该属于SMB 协议.因为该协议很复杂,所以是微软历史上出现安全问题最多的协议. 1.Nmap 最简单的方法:扫描其固定开放的端口139,445,但是无法准确判断其为windows系统 [email protected]:~# <strong

车联网开发日记12

今天是车联网开发的第14天,今天我们的任务是:预约加油功能的完善和车辆信息的统计.生成二维码和解析以及数据库的建立. 预约加油功能: 点击地图的加油站信息之后,可以跳转到预约加油界面. 车辆信息数据库:汽车品牌 car_brand型号  car_style车牌号 car_number发动机号 engine_number车身级别(几门几座)car_level里程数  car_driven汽油量 car_oil发动机性能(好.异常) car_engine变速器性能(好.异常) car_transmi

转: ㊣华哥日记㊣ 12.14-如何去了解更多的东西

经常有人私信问我怎么了解这么多东西的,如何安排时间学习.其实很简单啊,就五个字!跨行业交友.我们所在的圈子太狭窄了,你做外贸的,你周围多半都是搞外贸的,你们讨论的话题永远都是怎么拿询盘,怎么接待客户,怎么提高那其实毫不重要的一些东西,听着各种自己还是屌丝也敢到处讲课害人,你还当他是圣经宝典,你老师回家就睡出租屋,明早房东就来催房租.表面看你们好认真啊,天天学习交流,有毛用,这是在用表面勤奋麻痹自己的不足.几年后你发现自己很用功却在这个城市无立足之处,该你交智商税!你应该想想你的房东是怎么有10套

华哥日记-如何获取的好房源(转)

㊣华哥日记㊣ 12.30 前天说了怎么像股票定向增发那样找房源,今天说人人都可以获取的好房源,这个方法远比找一个好股票容易的多,只要你勤奋!在淡季和旺季其实都有便宜房源,淡季就不用说了,卖家都怕买家反悔,挂个楼盘出去半年没个看房的,这个时候市场普遍悲观,如果再遇到个公司缺钱发工资急用钱的业主,那你简直就是抢劫他了,这种情况房价会杀到你都不太敢买的价格,你会想尼玛是不是有什么问题,后面会不会市场还要跌,哈哈.这就是最佳买点一,买来只要等待市场复苏即可.旺季抢便宜货就难一些,因为卖家惜售,而且每成交

凡事预则立

听说--凡事预则立 吸取之前alpha冲刺的经验教训,也为了这次的bate冲刺可以更好更顺利地进行,更是为了迎接我们的新成员玮诗.我们开了一次组内会议,进行bate冲刺的规划. 上一张我们的合照: 具体会议议程如下: 1.讨论组长是否重选的议题和结论. 重新组队后,我们进行了组内评选组长的投票.投票结果如下: 成员 意向组长 周龙荣 李家鹏 李家鹏 周龙荣 曾玮诗 周龙荣 张柽 周龙荣 郑秦 周龙荣 在投票过程中,大家表示,在这段时间中,龙荣作为队长,为团队做了很多贡献,比如每次组织召集大家开会

vc编程中的20点小笔记

机器学习是一项经验技能,经验越多越好.在项目建立的过程中,实践是掌握机器学习的最佳手段.在实践过程中,通过实际操作加深对分类和回归问题的每一个步骤的理解,达到学习机器学习的目的. 预测模型项目模板不能只通过阅读来掌握机器学习的技能,需要进行大量的练习.本文将介绍一个通用的机器学习的项目模板,创建这个模板总共有六个步骤.通过本文将学到: 端到端地预测(分类与回归)模型的项目结构. 如何将前面学到的内容引入到项目中. 如何通过这个项目模板来得到一个高准确度的模板. 副诼匚盼胁臼匾膊讶赖期放判鼻懒合谖

【12.6】日记

12.6日记 线段树 HDU1540:单点修改+单点所在最长连续区间 思路:昨天用了set(平衡树)做的,发现简单的一批,还是学了一下线段树的做法.不过学完了之后发现,确实用线段树还是很有必要的,如果是区间修改的话,平衡树就挂了,就只能用线段树来处理了.更何况还有可能有区间所在最长连续区间之类的题目. 构造:每个节点存放4个值,lm记录当前区间,以左端点为左端点的最长1区间的长度,rm记录当前区间,以右端点为右端点的最长1区间的长度,mm记录当前区间最长1区间的长度,col是辅助数组,如果当前区

OpenGL学习日记-2014.12.21--光照

o(╯□╰)o深患中度拖延症,也是从开始写这篇笔记到结束居然用了一个月...虽然中间是发生了不少事,不过明明就有无数机会可以完成,就是拖着没写代码,各种借口...面对如此拖延症该如何是好QAQ 正文: 突然觉得这些日记写着写着就没什么意思...只是简单梳理一下书中的内容,没经过很多的思考,可不写心里更虚,怕自己几天就把看的书忘了.对于很多概念,都由于没有好好去写代码验证,而理解流于表面.对于光照这章也是下决心细细琢磨一番(现在才下的决心o(╯□╰)o),毕竟这很重要. 一.光照和颜色密切相关,光

AC日记——计算2的N次方 openjudge 1.6 12

12:计算2的N次方 总时间限制:  1000ms 内存限制:  65536kB 描述 任意给定一个正整数N(N<=100),计算2的n次方的值. 输入 输入一个正整数N. 输出 输出2的N次方的值. 样例输入 5 样例输出 32 提示 高精度计算 思路: 模拟: 来,上代码: #include<cstdio> using namespace std; int n; char s[101]; int main() { s[0]=1; scanf("%d",&n