关闭

[Ahoi2014]奇怪的计算器 解题报告

标签: 线段树数论
562人阅读 评论(0) 收藏 举报
分类:

感觉这是一道非常好的题,不过我看几乎所有人都是把它当傻逼题写的,为出题人感到遗憾。

一个很简单的性质是无论如何操作,每个数的相对大小是不变的,所以我们每次改变的都是一个区间。所以我们维护一个标记(k,b0,b1)表示对这个区间里的数x的操作为先*k,然后+b0x,然后+b1。这样的话对于当前在节点的标记(k,b0,b1),然后再加上一个新的标记(k,b0,b1),就变成(kk,kb0+b0,kb1+b1)。然后再记一下每个节点最左边和最右边的点的值就行了。

但是这个题的关键是k,b0,b1都可能会非常大,最大可能有109n。我看popoqqq的题解说数据很水没有爆long long,但是我assert了一下数据发现它其实爆了,但是为什么用int/long long写还能a呢?
这个其实是在O(1)快速乘中也有所用到的看似爆掉而实际没爆的神奇思想。
这是因为虽然k,b0,b1可能很大,但我们可以让它在模231意义下存在,这样的话我们就能求出它们运算的结果模231的值,而可以预见这个结果是在[L,R]之间的,也就是231以内。所以尽管我们在计算它的过程中使用了模运算,但是最终的结果其实是和不模一样的。
唯一需要long long的地方只在于找到>R的非法区间,其他的变量其实我们都用int,令其自然溢出即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
#include<cstdlib>
#include<algorithm>
const int N=1e5+5,Q=1e5+5;
typedef long long LL;
int L,R;

char * cp=(char *)malloc(3000000);
void in(int &x){
    while(*cp<'0'||*cp>'9')++cp;
    for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}
void in(char &c){
    while(*cp!='+'&&*cp!='-'&&*cp!='*'&&*cp!='@')++cp;
    c=*cp++;
}
char * os=(char *)malloc(2000000),*op=os;
void out(int x){
    if(x){
        out(x/10);
        *op++='0'+x%10;
    }
}

struct OS{
    char opt;
    int a;
}order[N];

struct QS{
    int x;
    int i;
    bool operator < (const QS & o)const{
        return x<o.x;
    }
}a[Q];
int ans[Q];

struct SS{
    int k,b0,b1;
    int l,r;
}segt[Q<<2];
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
void out(int node,int l,int r){
    printf("%d[%d,%d]={k=%d,b0=%d,b1=%d,l=%d,r=%d}\n",node,l,r,segt[node].k,segt[node].b0,segt[node].b1,segt[node].l,segt[node].r);
}
void paint(int node,int l,int r,int k,int b0,int b1){
    //printf("paint(%d,%d,%d,%I64d,%I64d,%I64d)\n",node,l,r,k,b0,b1);
    segt[node]=(SS){segt[node].k*k,segt[node].b0*k+b0,segt[node].b1*k+b1,segt[node].l*k+b0*a[l].x+b1,segt[node].r*k+b0*a[r].x+b1};
}
void pushdown(int node,int l,int r){
    if(segt[node].k!=1||segt[node].b0||segt[node].b1){
        paint(lson,segt[node].k,segt[node].b0,segt[node].b1),paint(rson,segt[node].k,segt[node].b0,segt[node].b1);
        segt[node].k=1,segt[node].b0=segt[node].b1=0;
    }
}
void pushup(int node){
    segt[node].l=segt[node<<1].l;
    segt[node].r=segt[node<<1|1].r;
    //printf("%d:%I64d,%I64d\n",node,segt[node<<1].l,segt[node<<1|1].r);
}
int cal(int data,int x,int k,int b0,int b1){
    return min((LL)k*data+(LL)b0*x+b1,R+1LL);
}
void build(int node,int l,int r){
    segt[node].k=1,segt[node].b0=segt[node].b1=0;
    if(l==r)segt[node].l=segt[node].r=a[l].x;
    else{
        build(lson),build(rson);
        pushup(node);
    }
}
void rquery(int node,int l,int r,int k,int b0,int b1){
    //printf("rquery(%d,[%d,%d],%d,%d,%d)\n",node,l,r,k,b0,b1);
    //printf("cal(%d)=%d\n",l,cal(segt[node].l,a[l].x,k,b0,b1));
    if(cal(segt[node].r,a[r].x,k,b0,b1)<=R)paint(node,l,r,k,b0,b1);
    else
        if(cal(segt[node].l,a[l].x,k,b0,b1)>R)paint(node,l,r,0,0,R);
        else{
            pushdown(node,l,r);
            if(cal(segt[node<<1].r,a[l+r>>1].x,k,b0,b1)>R){
                paint(rson,0,0,R);
                rquery(lson,k,b0,b1);
            }
            else{
                paint(lson,k,b0,b1);
                rquery(rson,k,b0,b1);
            }
            pushup(node);
        }
    //out(node,l,r);
}
void lquery(int node,int l,int r,int k,int b0,int b1){
    if(cal(segt[node].l,a[l].x,k,b0,b1)>=L)paint(node,l,r,k,b0,b1);
    else
        if(cal(segt[node].r,a[r].x,k,b0,b1)<L)paint(node,l,r,0,0,L);
        else{
            pushdown(node,l,r);
            if(cal(segt[node<<1|1].l,a[(l+r>>1)+1].x,k,b0,b1)<L){
                paint(lson,0,0,L);
                lquery(rson,k,b0,b1);
            }
            else{
                paint(rson,k,b0,b1);
                lquery(lson,k,b0,b1);
            }
            pushup(node);
        }
    //out(node,l,r);
}
void query(int node,int l,int r){
    if(l==r)ans[a[l].i]=segt[node].l;
    else{
        pushdown(node,l,r);
        query(lson),query(rson);
    }
}
int main(){
    freopen("calc8.in","r",stdin);
    freopen("bzoj_3878.out","w",stdout);
    fread(cp,1,3000000,stdin);
    int n;
    in(n),in(L),in(R);
    for(int i=1;i<=n;++i)in(order[i].opt),in(order[i].a);
    int q;
    in(q);
    for(int i=1;i<=q;++i){
        in(a[i].x);
        a[i].i=i;
    }
    sort(a+1,a+q+1);
    build(1,1,q);
    for(int i=1;i<=n;++i)
        switch(order[i].opt){
            case '+':
                rquery(1,1,q,1,0,order[i].a);
                break;
            case '-':
                lquery(1,1,q,1,0,-order[i].a);
                break;
            case '*':
                rquery(1,1,q,order[i].a,0,0);
                break;
            case '@':
                rquery(1,1,q,1,order[i].a,0);
                break;
        }
    query(1,1,q);
    for(int i=1;i<=q;++i){
        if(ans[i])out(ans[i]);
        else *op++='0';
        *op++='\n';
    }
    fwrite(os,1,op-os,stdout);
}

总结:
①一定要算好量的范围!!
②对于中间量可能很大,结果量很小的情况,我们可以在模意义下去运算它。——参考O(1)快速乘的思想。

0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

ACM气球膨胀问题C++实现

1.问题描述:给定一个矩形,在该矩形中有3个固定的点,以这3个点为中心的气球先后膨胀:膨胀时触碰到矩形的边或其他气球时则停止膨胀。编写程序求以何种顺序膨胀气球时,才能使气球的横切面面积之和为最大。 ...
  • cordova
  • cordova
  • 2016-03-11 09:18
  • 1122

BZOJ 3878 Ahoi2014 奇怪的计算器 线段树

题目大意:给定n个操作,每个操作有四种形式,操作之后若R就变成R,现在给定q个输入,求他们的输出 n,q 将这q个数建立线段树,四个操作都可以在线段树上完成 但是溢出怎么办呢? 容易发现若x ...
  • PoPoQQQ
  • PoPoQQQ
  • 2015-03-01 22:33
  • 1512

BZOJ3878 [Ahoi2014]奇怪的计算器 线段树

BZOJ3878 [Ahoi2014]奇怪的计算器Description对于一个储存数据大小只有[L,R]的计算器,有4个操作, 1、+ a:表示将当前的结果加上a; 2、- a:表示将当前的结果...
  • YJSchaf
  • YJSchaf
  • 2017-04-10 22:41
  • 1913

[Ahoi2013]作业 解题报告

很久以前用奇葩做法搞的题。。现在补个解题报告。传统做法: 考虑莫队,就需要我们设计一个O(1)O(1)插入,O(n√)O(\sqrt n)查询的数据结构,显然就需要对权值分块,记每个数出现次数和每个...
  • TA201314
  • TA201314
  • 2016-04-22 17:00
  • 649

AHOI2017初中组解题报告

总的来说呢,算是考废了吧。。。第三题花了太多时间,最后发现标程长度只有我那个20分的程序的四分之一。。。想的太复杂啦。。。T1【题目描述】 一个n*n的网格图上有m个探测器,每个探测器有个探测半径r...
  • qq_31640513
  • qq_31640513
  • 2017-04-16 12:17
  • 1184

BZOJ 1269 [AHOI 2006] STL(rope)\SPLAY 解题报告

1269: [AHOI2006]文本编辑器editorDescription这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个简单而高效的文本编辑器。你能帮助他吗?为了明确任务目标,可可对“...
  • onepointo
  • onepointo
  • 2017-07-20 20:46
  • 127

[Ahoi2008]Rectangle 解题报告

又是喜闻乐见的只会傻逼做法的题。。跟我跑得差不多快的人都写了1K,我写了快4K。。 并不知道他们怎么搞的,说下我的做法: 考虑按x从大到小的扫描线,每次在矩形的左下角(x1,y1)(x_1,y_1...
  • TA201314
  • TA201314
  • 2016-04-21 21:54
  • 380

2014NOIP普及组解题报告

  • 2015-01-30 10:06
  • 38KB
  • 下载

【线段树】[BZOJ3787][AHOI2014]奇怪的计算器

题目描述Description【故事背景】JYY有个奇怪的计算器,有一天这个计算器坏了,JYY希望你能帮助他写 一个程序来模拟这个计算器的运算。 【问题描述】 JYY的计算器可以执行N条预设好的...
  • JeremyGJY
  • JeremyGJY
  • 2016-04-07 17:53
  • 461

【bzoj3878】【AHOI2014】【奇怪的计算器】【线段树】

Description  【故事背景】 JYY有个奇怪的计算器,有一天这个计算器坏了,JYY希望你能帮助他写 一个程序来模拟这个计算器的运算。 【问题描述】 JYY的计算器可以执行N条预设好的指令。...
  • sunshinezff
  • sunshinezff
  • 2016-05-17 09:20
  • 370
    个人资料
    • 访问:166069次
    • 积分:3545
    • 等级:
    • 排名:第10764名
    • 原创:187篇
    • 转载:1篇
    • 译文:0篇
    • 评论:25条
    最新评论