【nodgd电路图B】线段树维护函数变换
这种题现在只见过两次,每次都觉得还是很神奇的,,,主要还是我太弱了orz orz orz
网上找不到链接和文字题面(NK的同学可以做到哦),简述题意:现在有横着一排n个电阻(n<=2.5*10^5),现在有m个操作(m<=2.5*10^5),1查询区间最小,2查询区间最大,3给区间每一个电阻串联上一个电阻值为R电阻,4给区间的每一个电阻并联上一个电阻值为R电阻。
物理题orz orz orz,做出来感谢hdhd和OBlack大佬的提示与帮助。我们可以将其每次变换转换为一个函数,(Ax + B / Cx + D) ,然后如果先执行某个函数再执行某个函数,就是将第一个函数代入第二个函数中,显然又会得到一个同阶的函数(因为这是一个线性的函数),这就是一次函数变换。
显然,对于一个电阻串联上一个新的电阻(阻值为R)的函数应该为(A=1,B=R,C=0,D=1),同时对于并联函数应该是(A=R,B=0,C=1,D=R)这样就可以完美表达出变换了。
之后就是一个区间极大值和区间极小值,区间覆盖函数,lazy putdowm函数下去的线段树问题了。有个毒点,函数A,B,C,D可能在多次变换后会变得极大导致精度有鬼,所以我们完全可以将A调整成1,其他同时/A,将他们的数据范围拉回来。
对于这些函数变换题的函数形式不是卡死的,就比如这次就是分式,所以应该及时调整思维。
code:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn = 500005; int ls[maxn],rs[maxn],tot,rt; double maxr[maxn],minr[maxn]; bool lazb[maxn]; struct node { double a,b,c,d; }ttmp,lazy[maxn]; inline void gaoji(int &p) { if(maxr[p]<minr[p]) swap(maxr[p],minr[p]); } char buf[1<<20],*p1,*p2; #define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++) inline int R() { char t=GC; int x=0; while(!isdigit(t)) t=GC; while(isdigit(t)) x=x*10+t-48,t=GC; return x; } void bianhuan(node &x,node &aa,node &bb) { ttmp.a = aa.a*bb.a + aa.c*bb.b; ttmp.b = aa.b*bb.a + aa.d*bb.b; ttmp.c = aa.a*bb.c + aa.c*bb.d; ttmp.d = aa.b*bb.c + aa.d*bb.d; ttmp.b /= ttmp.a; ttmp.c /= ttmp.a; ttmp.d /= ttmp.a; ttmp.a = 1.0; x = ttmp; } int n; void chuanlian(node &x,int rr) { x.a=1; x.b=1.0*rr; x.c=0; x.d=1.0; } void binglian(node &x,int rr) { x.a=1.0*rr; x.b=0; x.c=1; x.d=1.0*rr; } double dairu(double x,node &hs) { return ( ( hs.a * x + hs.b) / (hs.c * x + hs.d) ); } void putdowm(int x) { if(!lazb[x]||(!ls[x])) return; maxr[ls[x]] = dairu(maxr[ls[x]],lazy[x]); minr[ls[x]] = dairu(minr[ls[x]],lazy[x]); gaoji(ls[x]); if(!lazb[ls[x]]) lazb[ls[x]]=1,lazy[ls[x]]=lazy[x]; else bianhuan(lazy[ls[x]],lazy[ls[x]],lazy[x]); maxr[rs[x]] = dairu(maxr[rs[x]],lazy[x]); minr[rs[x]] = dairu(minr[rs[x]],lazy[x]); gaoji(rs[x]); if(!lazb[rs[x]]) lazb[rs[x]]=1,lazy[rs[x]]=lazy[x]; else bianhuan(lazy[rs[x]],lazy[rs[x]],lazy[x]); lazb[x] = 0; } inline void update(int p) { maxr[p] = max(maxr[ls[p]],maxr[rs[p]]); minr[p] = min(minr[ls[p]],minr[rs[p]]); } void maketree(int &p,int l,int r) { p = ++tot; if(l==r) { int x; x=R(); minr[p] = maxr[p] = (double)x; return; } int mid = (l+r)>>1; maketree(ls[p],l,mid); maketree(rs[p],mid+1,r); update(p); } double querymax(int p,int l,int r,int x,int y) { if(x<=l&&r<=y) return maxr[p]; putdowm(p); int mid = (l+r)>>1; if(y<=mid) return querymax(ls[p],l,mid,x,y); else if(x>mid) return querymax(rs[p],mid+1,r,x,y); else return max(querymax(ls[p],l,mid,x,y),querymax(rs[p],mid+1,r,x,y)); } double querymin(int p,int l,int r,int x,int y) { if(x<=l&&r<=y) return minr[p]; putdowm(p); int mid = (l+r)>>1; if(y<=mid) return querymin(ls[p],l,mid,x,y); else if(x>mid) return querymin(rs[p],mid+1,r,x,y); else return min(querymin(ls[p],l,mid,x,y),querymin(rs[p],mid+1,r,x,y)); } void add(int p,int l,int r,int x,int y,node &d) { if(x<=l&&r<=y) { maxr[p] = dairu(maxr[p],d); minr[p] = dairu(minr[p],d); if(!lazb[p]) { lazy[p] = d;lazb[p]=1; } else bianhuan(lazy[p],lazy[p],d); gaoji(p); ; return; } int mid = (l+r)>>1; putdowm(p); if(y<=mid) add(ls[p],l,mid,x,y,d); else if(x>mid) add(rs[p],mid+1,r,x,y,d); else add(ls[p],l,mid,x,y,d),add(rs[p],mid+1,r,x,y,d); update(p); } int main() { n=R(); maketree(rt,1,n); int m; m=R(); for(int i=1;i<=m;i++) { int opt,l,r,orz; opt=R();l=R();r=R(); switch(opt) { case 1:{ printf("%.14lf\n",querymax(rt,1,n,l,r)); break; } case 2:{ printf("%.14lf\n",querymin(rt,1,n,l,r)); break; } case 3:{ orz=R(); node tmp; chuanlian(tmp,orz); add(1,1,n,l,r,tmp); break; } case 4:{ orz=R(); node tmp; binglian(tmp,orz); add(1,1,n,l,r,tmp); break; } } } }