[Ahoi2014]奇怪的计算器 解题报告
2016-06-01 22:15
483 查看
感觉这是一道非常好的题,不过我看几乎所有人都是把它当傻逼题写的,为出题人感到遗憾。
一个很简单的性质是无论如何操作,每个数的相对大小是不变的,所以我们每次改变的都是一个区间。所以我们维护一个标记(k,b0,b1)表示对这个区间里的数x的操作为先*k,然后+b0x,然后+b1。这样的话对于当前在节点的标记(k,b0,b1),然后再加上一个新的标记(k′,b′0,b′1),就变成(k∗k′,k′∗b0+b′0,k′∗b1+b′1)。然后再记一下每个节点最左边和最右边的点的值就行了。
但是这个题的关键是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,令其自然溢出即可。
总结:
①一定要算好量的范围!!
②对于中间量可能很大,结果量很小的情况,我们可以在模意义下去运算它。——参考O(1)快速乘的思想。
一个很简单的性质是无论如何操作,每个数的相对大小是不变的,所以我们每次改变的都是一个区间。所以我们维护一个标记(k,b0,b1)表示对这个区间里的数x的操作为先*k,然后+b0x,然后+b1。这样的话对于当前在节点的标记(k,b0,b1),然后再加上一个新的标记(k′,b′0,b′1),就变成(k∗k′,k′∗b0+b′0,k′∗b1+b′1)。然后再记一下每个节点最左边和最右边的点的值就行了。
但是这个题的关键是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 ; 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)快速乘的思想。
相关文章推荐
- 小知识点记录
- ispunct字符串测试函数应用实例
- 全息投影技术及其实现(附素材下载)
- 设计模式学习笔记--中介者模式
- update与delete多表联接问题
- webkit webApp 开发技术要点总结
- 枚举
- 给一个整数N,N可以拆分出不同的因子,而且满足因子的乘积等于N。则在这些因子中和是最小的一组是什么
- IndentationError: unindent does not match any outer indentation level
- php 安装 sphinx
- Java之多线程
- 【hdu】2433 Travel【最短路删边】
- Linux磁盘管理基本配置
- 第二次冲刺阶段站立会议(九)
- MySQL(Navicat)运行.sql文件时报错:[Err] 2006 - MySQL server has gone away 的解决方法
- c语言:int与char转换示例
- 自加作为循环的执行条件时的注意事项
- nc命令小结
- 当谨慎变成了常态
- Windows 64位下安装Redis教程