来自FallDream的博客,未经允许,请勿转载,谢谢。
话说这一段时间算是过去了,好久好久之后终于又有联考了 没想到这次到我们学校出题,昨天才想起来,临时花一天赶了一套,我出了一个sbFFT,质量勉强吧。
upd:代码已经更新
A.种树
有一棵二叉树,有点权,你要从节点1出发前往一个节点,如果路上的节点权值和他相等就结束,否则比他小向左,比他大向右,问能不能找到。
需要支持多组询问,单点修改,子树反转
n,m<=10^5
#include<iostream> #include<cstdio> #define pa pair<int,int> #define mp(x,y) make_pair(x,y) #define MN 100000 #define INF 2000000000 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } struct data{int lmn,lmx,rmn,rmx; friend data operator +(data a,data b) { data c; c.lmn=min(a.lmn,b.lmn); c.rmx=max(a.rmx,b.rmx); c.lmx=(a.lmx==INF)?b.lmx:(b.lmx==INF?a.lmx:max(a.lmx,b.lmx)); c.rmn=(a.rmn==-INF)?b.rmn:(b.rmn==-INF?a.rmn:min(a.rmn,b.rmn)); return c; } void rev() { swap(lmn,rmn);if(lmn==-INF) lmn=INF;if(rmn==INF) rmn=-INF; swap(lmx,rmx);if(rmx==INF) rmx=-INF;if(lmx==-INF)lmx=INF; } }; struct Tree{int l,r,rev;data x;}T[MN*4+5]; int l[MN+5],r[MN+5],num[MN+5],fa[MN+5],top[MN+5],n,m,w[MN+5],dn=0,nl[MN+5],nr[MN+5],size[MN+5]; pa p[MN+5]; void Dfs1(int x) { if(l[x]) Dfs1(l[x]),fa[l[x]]=x; if(r[x]) Dfs1(r[x]),fa[r[x]]=x; size[x]=size[l[x]]+size[r[x]]+1; } void Dfs2(int x,int tp) { if(!x) return;top[x]=tp;num[nl[x]=++dn]=x; if(size[l[x]]>size[r[x]]) Dfs2(l[x],tp),Dfs2(r[x],r[x]); else Dfs2(r[x],tp),Dfs2(l[x],l[x]); p[l[x]]=mp(1,w[x]);p[r[x]]=mp(2,w[x]); nr[x]=dn; } void build(int x,int l,int r) { if((T[x].l=l)==(T[x].r=r)) { int now=num[l]; if(p[now].first==1) T[x].x=(data){p[now].second,p[now].second,-INF,-INF}; else T[x].x=(data){INF,INF,p[now].second,p[now].second}; return; } int mid=l+r>>1; build(x<<1,l,mid);build(x<<1|1,mid+1,r); T[x].x=T[x<<1].x+T[x<<1|1].x; } inline void Mark(int x) { T[x].rev^=1; T[x].x.rev(); } inline void pushdown(int x) { Mark(x<<1);Mark(x<<1|1); T[x].rev=0; } void Modify(int x,int v,pa k) { if(T[x].l==T[x].r) { if(k.first==1^T[x].rev) T[x].x=(data){k.second,k.second,-INF,-INF}; else T[x].x=(data){INF,INF,k.second,k.second}; return; } if(T[x].rev) pushdown(x); int mid=(T[x].l+T[x].r)>>1; if(v<=mid) Modify(x<<1,v,k); else if(v>mid) Modify(x<<1|1,v,k); T[x].x=T[x<<1].x+T[x<<1|1].x; } void Reverse(int x,int l,int r) { if(T[x].l==l&&T[x].r==r){Mark(x);return;} int mid=(T[x].l+T[x].r)>>1; if(T[x].rev) pushdown(x); if(r<=mid) Reverse(x<<1,l,r); else if(l>mid) Reverse(x<<1|1,l,r); else Reverse(x<<1,l,mid),Reverse(x<<1|1,mid+1,r); T[x].x=T[x<<1].x+T[x<<1|1].x; } data Query(int x,int l,int r) { if(T[x].l==l&&T[x].r==r) return T[x].x; if(T[x].rev) pushdown(x); int mid=(T[x].l+T[x].r)>>1; if(r<=mid) return Query(x<<1,l,r); else if(l>mid) return Query(x<<1|1,l,r); else return Query(x<<1,l,mid)+Query(x<<1|1,mid+1,r); } data Solve(int x) { data res=(data){INF,INF,-INF,-INF}; for(;x;x=fa[top[x]]) res=res+Query(1,nl[top[x]],nl[x]); return res; } int main() { n=read();m=read(); for(int i=1;i<=n;++i) w[i]=read(),l[i]=read(),r[i]=read(); Dfs1(1);Dfs2(1,1);p[1]=mp(1,INF);build(1,1,n); for(int i=1;i<=m;++i) { int op=read(),x=read(); if(op==1) { int y=read(); if(l[x]) Modify(1,nl[l[x]],mp(1,y)); if(r[x]) Modify(1,nl[r[x]],mp(2,y)); w[x]=y; } if(op==2) { if(nl[x]!=nr[x]) Reverse(1,nl[x]+1,nr[x]); } if(op==3) { data y=Solve(x); if(w[x]<y.lmn&&w[x]>y.rmx) puts("YES"); else puts("NO"); } } return 0; }
把一条边看成一个限制条件,也就是大于或者小于某个值。那么树链剖分之后线段树维护就行了,维护区间小于限制的最大/最小值,大于限制的最大/最小值。
子树反转可以打标记实现。复杂度nlog^2n
代码貌似丢在学校了,端午过后补吧。
B.mark
C.秋之国的盛会
给定两个长度为n的序列ai,bi,可以把a向后推动0到n次。给定c,m,推动k次的时候的费用是$sum{(frac{ai}{bi}-c*k-m)^2}$
n<=10^5
求最小费用
把这个式子展开,很容易发现只要能快速求出不同推动状态下的$sum{frac{ai}{bi}}$和$sum{(frac{ai}{bi})^2}$即可。
把ai数组倒过来并复制一遍,b数组变成1/bi,fft求卷积即可。另一项同理。
复杂度nlogn
#include<iostream> #include<cstdio> #include<cmath> #define pi acos(-1) #define ld long double #define MN 262144 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int N,n,M;double cost; struct cp { ld r,u; cp ( double x = 0,double y = 0) :r(x) , u(y) {} cp operator + (const cp&y) { return cp(r+y.r,u+y.u);} cp operator - (const cp&y) { return cp(r-y.r,u-y.u);} cp operator * (const cp&y) { return cp(r*y.r-u*y.u,r*y.u+u*y.r);} cp operator * (ld y){return cp(r*y,u*y);} cp operator / (ld y){return cp(r/y,u/y);} }A[MN+5],B[MN+5],C[MN+5],a[MN+5],b[MN+5],c[MN+5],w[2][MN+5]; void fft(cp*x,int r) { for(int i=0,j=0;i<N;++i) { if(i>j) swap(x[i],x[j]); for(int l=N>>1;(j^=l)<l;l>>=1); } for(int i=2;i<=N;i<<=1)for(int j=0;j<N;j+=i)for(int k=0;k<i>>1;++k) { cp t=x[j+k+(i>>1)]*w[r][N/i*k]; x[j+k+(i>>1)]=x[j+k]-t; x[j+k]=x[j+k]+t; } if(r)for(int i=0;i<N;++i) x[i]=x[i]/N; } int main() { freopen("party.in","r",stdin); freopen("party.out","w",stdout); n=read();M=read();scanf("%lf",&cost); for(int i=n-1;~i;--i) a[i].r=read(),A[i].r=a[i].r*a[i].r; for(int i=0;i<n;++i) b[i].r=(ld)1/read(),B[i].r=b[i].r*b[i].r; for(int i=0;i<n;++i) a[i+n]=a[i],A[i+n]=A[i]; for(N=1;N<=n<<1;N<<=1); w[0][0]=w[1][N]=cp(1,0);w[0][1]=w[1][N-1]=cp(cos(2*pi/N),sin(2*pi/N)); for(int i=2;i<=N;++i) w[0][i]=w[1][N-i]=w[0][i-1]*w[0][1]; fft(a,0);fft(b,0);fft(A,0);fft(B,0); for(int i=0;i<N;++i) c[i]=a[i]*b[i],C[i]=A[i]*B[i]; fft(c,1);fft(C,1); ld ans=1e99;int mx=0; for(int i=n-1;i<n<<1;++i) { ld j=cost*(i-n+1); if(C[i].r-2*c[i].r*(j+M)+(ld)n*(j+M)*(j+M)<ans) mx=i-n+2; ans=min(ans,C[i].r-2*c[i].r*(j+M)+(ld)n*(j+M)*(j+M)); } printf("%.10lf",(double)ans); return 0; }