@Cyani
2019-03-23T01:17:43.000000Z
字数 34750
阅读 825
利用了ull能承受18次10^18级别的数相加的性质。理论上支持最多只有 。否则需要及时取模
#void dft(poly &A,int n){static ll W[N<<1],*H[30],*las=W,mx=0;for(;mx<n;mx++){H[mx]=las;ll w=1,wn=power(g,(mod-1)>>(mx+1));REP(i,1<<mx)*las++=w,w=w*wn%mod;}static ll a[N];A.resize(1<<n);for(int i=0,j=0;i<(1<<n);++i){a[i]=A[j];for(int k=1<<(n-1);(j^=k)<k;k>>=1);}for(int k=0,d=1;k<n;k++,d<<=1)for(int i=0;i<(1<<n);i+=d<<1){ll *l=a+i,*r=a+i+d,*w=H[k],t;for(int j=0;j<d;++j,++l,++r){t=(*w++)*(*r)%mod;//t=(*w++)*(*r%mod)%mod;*r=*l+mod-t,*l+=t;}}REP(i,1<<n)A[i]=a[i]%mod;}
注意🐮迭之前之后都需要resize
const int mod=998244353;namespace Poly{const int N=(1<<20)+5,g=3;inline int power(int x,int p){int res=1;for(;p;p>>=1,x=(ll)x*x%mod)if(p&1)res=(ll)res*x%mod;return res;}inline int fix(const int x){return x>=mod?x-mod:x;}void dft(poly &A,int n){static ull W[N<<1],*H[30],*las=W,mx=0;for(;mx<n;mx++){H[mx]=las;ull w=1,wn=power(g,(mod-1)>>(mx+1));REP(i,1<<n)*las++=w,w=w*wn%mod;}if(A.size()!=(1<<n))A.resize(1<<n);static ull a[N];for(int i=0,j=0;i<(1<<n);++i){a[i]=A[j];for(int k=1<<(n-1);(j^=k)<k;k>>=1);}for(int k=0,d=1;k<n;k++,d<<=1)for(int i=0;i<(1<<n);i+=(d<<1)){ull *l=a+i,*r=a+i+d,*w=H[k],t;for(int j=0;j<d;j++,l++,r++){t=(*r)*(*w++)%mod;*r=*l+mod-t,*l+=t;}}REP(i,1<<n)A[i]=a[i]%mod;}void idft(poly &a,int n){a.resize(1<<n),reverse(a.begin()+1,a.end());dft(a,n);int inv=power(1<<n,mod-2);REP(i,1<<n)a[i]=(ll)a[i]*inv%mod;}poly FIX(poly a){while(!a.empty()&&!a.back())a.pop_back();return a;}poly add(poly a,poly b,int op=0){a.resize(max(a.size(),b.size()));REP(i,b.size())a[i]=fix(op?a[i]+mod-b[i]:a[i]+b[i]);return FIX(a);}poly mul(poly a,poly b,int t=1){if(t==1&&a.size()+b.size()<=24){poly c(a.size()+b.size(),0);REP(i,a.size())REP(j,b.size())c[i+j]=(c[i+j]+(ll)a[i]*b[j])%mod;return FIX(c);}int n=1,aim=a.size()*t+b.size();while((1<<n)<=aim)n++;dft(a,n),dft(b,n);if(t==1)REP(i,1<<n)a[i]=(ll)a[i]*b[i]%mod;else REP(i,1<<n)a[i]=(ll)a[i]*a[i]%mod*b[i]%mod;idft(a,n),a.resize(aim);return FIX(a);}poly mul(poly a,int b){REP(i,a.size())a[i]=(ll)a[i]*b%mod;return FIX(a);}poly inv(poly a,int n){ //a[0] != 0a.resize(n);poly b;if(n==1){b.pb(power(a[0],mod-2));return b;}b=inv(a,n+1>>1);b=add(mul(b,2),mul(b,a,2),1);return b.resize(n),b;}poly Der(poly a){REP(i,a.size()-1)a[i]=(ll)(i+1)*a[i+1]%mod;return a.pop_back(),a;}poly Int(poly a){static int inv[N];inv[1]=1;a.pb(0);rep(i,2,a.size())inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;per(i,a.size()-1,1)a[i]=(ll)a[i-1]*inv[i]%mod;return a[0]=0,a;}poly Ln(poly a,int n){ //a[0] = 1a=mul(Der(a),inv(a,n)),a.resize(n-1);return FIX(Int(a));}poly Exp(poly a,int n){ //a[0] = 0a.resize(n);poly b,one(1,1);if(n==1)return one;b=Exp(a,n+1>>1);b=mul(b,add(add(a,Ln(b,n),1),one));return b.resize(n),b;}poly Div(poly a,poly b){poly c;int n=a.size()-1,m=b.size()-1;if(n<m)return c;reverse(a.begin(),a.end());a.resize(n-m+1);reverse(b.begin(),b.end());b.resize(n-m+1);c=mul(a,inv(b,n-m+1));c.resize(n-m+1);return reverse(c.begin(),c.end()),c;}poly Mod(poly a,poly b){return FIX(add(a,mul(Div(a,b),b),1));}inline int chk(int x){return power(x,(mod-1)/2)==1;}inline int R(){return rand()%mod;}inline pii mul(pii a,pii b,int w){return mp(((ll)a.fi*b.fi+(ll)a.se*b.se%mod*w)%mod,((ll)a.fi*b.se+(ll)a.se*b.fi)%mod);}inline int Sqrt(int x){if(!chk(x))return -1;int a=R();while(chk(((ll)a*a-x+mod)%mod))a=R();int w=((ll)a*a-x+mod)%mod,p=(mod+1)/2;pii res=mp(1,0),t=mp(a,1);for(;p;p>>=1,t=mul(t,t,w))if(p&1)res=mul(res,t,w);assert(!res.se);return min(res.fi,mod-res.fi);}poly Sqrt(poly a,int n){if(n==1){poly b(1,Sqrt(a[0]));return b;}a.resize(n);poly b=Sqrt(a,n+1>>1);b=mul(add(b,mul(a,inv(b,n))),(mod+1)/2);return b.resize(n),b;}poly fastpow(poly a,ll k,int n) {a.resize(n),a=FIX(a);if(!a.size())return a;int st=0,base=0;while(!a[st])++st;if(st*k>=n)return a.resize(0),a;REP(i,a.size()-st)a[i]=a[i+st];if(st)a.resize(a.size()-st);base=a[0];ll inv=power(base,mod-2);REP(i,a.size())a[i]=a[i]*inv%mod;a=FIX(Exp(mul(Ln(a, n),k%mod),n));;if(st){reverse(a.begin(),a.end());a.resize(a.size()+st*k);reverse(a.begin(),a.end());a.resize(n),a=FIX(a);}base=power(base,k);REP(i,a.size())a[i]=(ll)a[i]*base%mod;return FIX(a);}#define lc (o<<1)#define rc (o<<1|1)#define mid ((l+r)>>1)poly owo[N<<1];void Qinit(int o,int l,int r,vi &x){if(l==r){owo[o].resize(2);owo[o][1]=1,owo[o][0]=mod-x[l];return;}Qinit(lc,l,mid,x),Qinit(rc,mid+1,r,x);if(o>=2)owo[o]=mul(owo[lc],owo[rc]);}void Q(int o,int l,int r,poly f,vi &y){if(l==r){y[l]=(f.empty()?0:f[0]);return;}Q(lc,l,mid,Mod(f,owo[lc]),y);Q(rc,mid+1,r,Mod(f,owo[rc]),y);}void Q(int n,int m,poly f,vi &x,vi &y){ //初始化大小!Qinit(1,0,m-1,x),Q(1,0,m-1,f,y);}poly ovo[N<<1];void Cinit(int o,int l,int r,vi &x){if(l==r){ovo[o].resize(2);ovo[o][1]=1,ovo[o][0]=mod-x[l];return;}Cinit(lc,l,mid,x),Cinit(rc,mid+1,r,x);ovo[o]=mul(ovo[lc],ovo[rc]);}poly C(int o,int l,int r,vi &z){if(l==r){poly res(1,z[l]);return res;}return add(mul(C(lc,l,mid,z),ovo[rc]),mul(C(rc,mid+1,r,z),ovo[lc]));}poly C(int n,vi &x,vi &y){ //初始化大小!Cinit(1,0,n-1,x);poly M=Der(ovo[1]),z(n,0);Q(n,n,M,x,z);REP(i,n)z[i]=(ll)y[i]*power(z[i],mod-2)%mod;poly res=C(1,0,n-1,z);return res.resize(n),res;}#undef lc#undef rc#undef mid}using namespace Poly;
玄学方法可以优化到4次dft,不过似乎只快了20%
const int mod=1000000007;const int N=200005,M=(1<<15);const ld PI=acos(-1);struct C{ld r,i;inline friend C operator + (const C &a,const C &b){return (C){a.r+b.r,a.i+b.i};}inline friend C operator - (const C &a,const C &b){return (C){a.r-b.r,a.i-b.i};}inline friend C operator * (const C &a,const C &b){return (C){a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r};}inline friend C operator / (const C &a,const ld &b){return (C){a.r/b,a.i/b};}inline C rev(){return (C){r,-i};}};const C owo1=(C){0.5,0},owo2=(C){0,-0.5},I=(C){0,1};namespace Poly{const int N=(1<<19)+5;poly naive_mul(const poly &A,const poly &B){poly C(A.size()+B.size()-1,0);REP(i,A.size())REP(j,B.size())C[i+j]=(C[i+j]+(ll)A[i]*B[j])%mod;return C;}void dft(C *a,int n){static C W[N<<1],*H[30],*las=W;static int R[N],mx=0;for(;mx<n;++mx){H[mx]=las;int m=(1<<(mx+1));REP(i,1<<mx)*las++=(C){cos(2*PI*i/m),sin(2*PI*i/m)};}REP(i,1<<n){R[i]=(i&1?R[i^1]|1<<(n-1):R[i>>1]>>1);if(i<R[i])swap(a[i],a[R[i]]);}for(int k=0,d=1;k<n;++k,d<<=1)for(int i=0;i<(1<<n);i+=(d<<1)){C *l=a+i,*r=a+i+d,*w=H[k],x,y;for(int j=0;j<d;++j,++l,++r){x=*l,y=(*r)*(*w++);*l=x+y,*r=x-y;}}}void idft(C *a,int n){//reverse(a+1,a+(1<<n));dft(a,n);int m=1<<n;REP(i,m)a[i]=a[i]/m;}poly mul(const poly &A,const poly &B){int aim=(A.size()+B.size()-1),n=1;if(aim<=32)return naive_mul(A,B);while((1<<n)<=aim)++n;C *a=new C[1<<n],*b=new C[1<<n];C *c=new C[1<<n],*d=new C[1<<n];REP(i,1<<n){if(i<A.size())a[i]=(C){ld(A[i]>>15),ld(A[i]&(M-1))};else a[i]=(C){0,0};if(i<B.size())b[i]=(C){ld(B[i]>>15),ld(B[i]&(M-1))};else b[i]=(C){0,0};}dft(a,n),dft(b,n);REP(i,1<<n)c[i]=a[i],d[i]=b[i];REP(i,1<<n){int j=(!i?0:(1<<n)-i);static C a1,a2,b1,b2;a1=owo1*(c[i]+c[j].rev());a2=owo2*(c[i]-c[j].rev());b1=owo1*(d[i]+d[j].rev());b2=owo2*(d[i]-d[j].rev());a[j]=a1*b1+I*a1*b2;b[j]=a2*b1+I*a2*b2;}idft(a,n),idft(b,n);poly C(aim,0);REP(i,aim){int a1=(ll)(a[i].r+0.5)%mod;int a2=(ll)(a[i].i+0.5)%mod;int b1=(ll)(b[i].r+0.5)%mod;int b2=(ll)(b[i].i+0.5)%mod;C[i]=(((ll)a1<<30)+((ll)a2<<15)+((ll)b1<<15)+b2)%mod;}delete [] a;delete [] b;delete [] c;delete [] d;return C;}}
维护子树信息。注意虚实边切换对答案的影响
共价大爷游长沙
const int N=500005;int t[N][2],fa[N],rev[N];ui s[N],w[N],is[N];inline int isr(int x){return t[fa[x]][0]!=x&&t[fa[x]][1]!=x;}inline void up(int x){s[x]=s[t[x][0]]^s[t[x][1]]^is[x]^w[x];}inline void Rev(int x){if(!x)return;swap(t[x][0],t[x][1]),rev[x]^=1;}inline void down(int x){if(rev[x])Rev(t[x][0]),Rev(t[x][1]),rev[x]=0;}#define B(x) (t[fa[x]][1]==x)inline void rot(int x){int y=fa[x],z=fa[y],f=B(x);fa[t[y][f]=t[x][f^1]]=y;if(!isr(y))t[z][B(y)]=x;fa[x]=z;fa[t[x][f^1]=y]=x,up(y),up(x);}inline void push(int x){if(!isr(x))push(fa[x]);down(x);}inline void spl(int x){push(x);for (;!isr(x);rot(x)){if(!isr(fa[x]))rot(B(x)^B(fa[x])?x:fa[x]);}}inline void access(int x){int y=0;while(x){spl(x),is[x]^=s[t[x][1]],is[x]^=s[t[x][1]=y],up(x);y=x,x=fa[x];}}inline void evert(int x){access(x),spl(x),Rev(x);}inline void cut(int x,int y){evert(x),access(y),spl(y);t[y][0]=fa[x]=0,up(y);}inline void link(int x,int y){evert(x),access(y),spl(y);fa[x]=y,is[y]^=s[x],up(y);}inline void upd(int x,ui y){access(x),spl(x),w[x]^=y,up(x);}inline ui qry(int x,int y){return evert(x),access(y),spl(y),s[x];}
维护凸包(最好离线CDQ解决..)注意维护点的偏序关系。
namespace Cov {/*实现接口:init, 初始化insert(point p), 插入点p,并动态维护上凸包query(point p), 询问凸包上与其点积最大的点*/const int N = 250005;int lc[N], rc[N], sz[N], fix[N];int pre[N], suf[N];point key[N];int tot, rt;inline int nw(point p) {int o = ++ tot;key[o] = p, fix[o] = rand(),lc[o] = rc[o] = pre[o] = suf[o] = 0, sz[o] = 1;return o;}inline void up(int o) {sz[o] = sz[lc[o]] + sz[rc[o]] + 1;}int make(int x, int y) {if (!x || !y) return x | y;return fix[x] > fix[y]? (rc[x] = make(rc[x], y), up(x), x): (lc[y] = make(x, lc[y]), up(y), y);}pii split(int x, point k) {if (!x) return make_pair(0, 0);pii y; key[x] <= k? (y = split(rc[x], k), rc[x] = y.fi, y.fi = x): (y = split(lc[x], k), lc[x] = y.se, y.se = x);return up(x), y;}inline int kth(int x, int k) {if (!x) return 0;for (;;) {int lsz = sz[lc[x]] + 1;if (lsz == k) return x;if (lsz > k) x = lc[x];else k -= lsz, x = rc[x];}}point getL(int u, point p) {if (!u) return point(-inf, -inf);if (!pre[u] || cross(key[pre[u]], key[u], p) < 0)return max(key[u], getL(rc[u], p));return getL(lc[u], p);}point getR(int u, point p) {if (!u) return point(inf, inf);if (!suf[u] || cross(key[suf[u]], key[u], p) > 0)return min(key[u], getR(lc[u], p));return getR(rc[u], p);}inline void init() {tot = rt = 0;}inline void insert(point p) {pii x = split(rt, p);int a = kth(x.fi, sz[x.fi]), b = suf[a];if (a && b && cross(key[a], p, key[b]) >= 0||a&&key[a]==p) {rt = make(x.fi, x.se);return;}point L = getL(x.fi, p), R = getR(x.se, p);pii y = split(x.fi, L), z = split(x.se, point(R.x, R.y - 1));a = kth(y.fi, sz[y.fi]), b = kth(z.se, 1);int o = nw(p);pre[o] = a, suf[o] = b;if (a) suf[a] = o;if (b) pre[b] = o;rt = make(y.fi, make(o, z.se));}ll dfs(int u, point p) {ll res = dot(p, key[u]);if (rc[u] && dot(p, key[suf[u]]) >= res) return dfs(rc[u], p);if (lc[u] && dot(p, key[pre[u]]) >= res) return dfs(lc[u], p);return res;}inline ll query(point p) {return rt ? dfs(rt, p) : 0;}}
可持久化
只需要类似主席树那样拷贝即可。
namespace treap{int lc[N],rc[N],sz[N],key[N],val[N];int size=0;inline int nw(int x){int o=++size;key[o]=rand();val[o]=x;sz[o]=1;return o;}inline int copy(int x){int o=nw(val[x]);key[o]=key[x];sz[o]=sz[x];lc[o]=lc[x];rc[o]=rc[x];return o;}inline void up(int o){sz[o]=sz[lc[o]]+1+sz[rc[o]];}inline int merge(int x,int y){if(!x||!y)return x|y;if(key[x]<key[y]){int o=copy(x);rc[o]=merge(rc[o],y);return up(o),o;}else{int o=copy(y);lc[o]=merge(x,lc[o]);return up(o),o;}}inline pii split(int o,int k){if(!o)return mp(0,0);if(k==0)return mp(0,o);if(k==sz[o])return mp(o,0);if(k<=sz[lc[o]]){int u=copy(o);pii y=split(lc[u],k);lc[u]=y.se;return up(u),mp(y.fi,u);}else{int u=copy(o);pii y=split(rc[u],k-1-sz[lc[u]]);rc[u]=y.fi;return up(u),mp(u,y.se);}}inline int ins(int o,int k,int x){pii y=split(o,k-1);return merge(y.fi,merge(nw(x),y.se));}inline int del(int o,int k){pii y=split(o,k-1);pii z=split(y.se,1);return merge(y.fi,z.se);}inline int qry(int o,int k){for(;;){if(k==sz[lc[o]]+1)return val[o];if(k<=sz[lc[o]])o=lc[o];else k-=1+sz[lc[o]],o=rc[o];}}}
线段树嵌套treap
注意treap的懒标记是全局的,所以也需要在线段树上放
const int N=250000*50;namespace treap{struct node{int lc,rc,key,sz,tag,val,best,lzy;}T[N];int stk[N],siz,top;inline int nw(int p,int owo){int o;if(top)o=stk[top--];else o=++siz;T[o].lc=T[o].rc=T[o].lzy=0;T[o].key=rand();T[o].sz=1;T[o].tag=p;T[o].val=owo;T[o].best=owo;return o;}inline void push(int o){stk[++top]=o;}inline void plus(int o,int x){if(o)T[o].tag+=x,T[o].lzy+=x;}inline void down(int o){if(!T[o].lzy)return;if(T[o].lc)plus(T[o].lc,T[o].lzy);if(T[o].rc)plus(T[o].rc,T[o].lzy);T[o].lzy=0;}inline void up(const int &o){T[o].sz=T[T[o].lc].sz+1+T[T[o].rc].sz;T[o].best=max(T[o].val,max(T[T[o].lc].best,T[T[o].rc].best));}inline int make(int x,int y){if(!x||!y)return x|y;down(x),down(y);if(T[x].key<T[y].key){T[x].rc=make(T[x].rc,y);return up(x),x;}T[y].lc=make(x,T[y].lc);return up(y),y;}pii split(int o,int k){if(!o)return mp(0,0);down(o);if(k<T[o].tag){pii y=split(T[o].lc,k);T[o].lc=y.se,y.se=o;return up(o),y;}pii y=split(T[o].rc,k);T[o].rc=y.fi,y.fi=o;return up(o),y;}int count(int &rt,int l,int r){if(!rt)return 0;//printf("@ %d %d %d\n",rt,l,r);pii x=split(rt,l-1);pii y=split(x.se,r);int res=T[y.fi].sz;rt=make(x.fi,make(y.fi,y.se));return res;}int getmx(int &rt,int l,int r){if(!rt)return 0;pii x=split(rt,l-1);pii y=split(x.se,r);int res=T[y.fi].best;rt=make(x.fi,make(y.fi,y.se));return res;}void add(int &rt,int a,int b){pii x=split(rt,a-1);rt=make(x.fi,make(nw(a,b),x.se));}void chg(int &rt,int a,int b){pii x=split(rt,a-1);pii y=split(x.se,a);down(y.fi),T[y.fi].val=b,up(y.fi);rt=make(x.fi,make(y.fi,y.se));}int del(int &rt,int a){pii x=split(rt,a-1);pii y=split(x.se,a);int res=T[y.fi].val;push(y.fi);rt=make(x.fi,y.se);return res;}int find(int o,int k){if(!o)return 0;if(T[o].tag==k)return 1;down(o);if(k<T[o].tag)return find(T[o].lc,k);return find(T[o].rc,k);}}const int inf=1e6;const int M=1000000*25;namespace seg{int lc[M],rc[M],cur[M],shit[M];int stk[M],sz,top;inline int nw(){int o;if(top)o=stk[top--];else o=++sz;lc[o]=rc[o]=cur[o]=shit[o]=0;return o;}inline void push(int o){stk[++top]=o;}void plus(int o,int x){if(!o)return;treap::plus(cur[o],x);shit[o]+=x;}void down(int o){if(!shit[o])return;plus(lc[o],shit[o]);plus(rc[o],shit[o]);shit[o]=0;}#define mid ((l + r) >> 1)int build(int l,int r,int p,int A,int B){int o=nw();treap::add(cur[o],p,B);if(l==r)return o;if(A<=mid)lc[o]=build(l,mid,p,A,B);else rc[o]=build(mid+1,r,p,A,B);return o;}int del(int o,int l,int r,int p){if(!treap::find(cur[o],p))return 0;down(o);int owo=treap::del(cur[o],p);if(l==r)return owo;int tmp=del(lc[o],l,mid,p);int tmp2=del(rc[o],mid+1,r,p);return max(tmp,tmp2);}void ins(int &o,int l,int r,int p,int A,int B){if(!o)o=nw();down(o);treap::add(cur[o],p,B);if(l==r)return;if(A<=mid)ins(lc[o],l,mid,p,A,B);else ins(rc[o],mid+1,r,p,A,B);}void chgB(int o,int l,int r,int p,int nwB){if(!treap::find(cur[o],p))return;down(o);treap::chg(cur[o],p,nwB);if(l==r)return;chgB(lc[o],l,mid,p,nwB);chgB(rc[o],mid+1,r,p,nwB);}int merge(int x,int y,int l,int r){down(x),down(y);if(!x||!y)return x|y;cur[x]=treap::make(cur[x],cur[y]);if(l==r)return push(y),x;lc[x]=merge(lc[x],lc[y],l,mid);rc[x]=merge(rc[x],rc[y],mid+1,r);return push(y),x;}int qryA(int o,int l,int r,int x,int y,int L,int R){if(l==x&&y==r){return treap::count(cur[o],L,R);}down(o);if(y<=mid)return qryA(lc[o],l,mid,x,y,L,R);if(mid<x)return qryA(rc[o],mid+1,r,x,y,L,R);return qryA(lc[o],l,mid,x,mid,L,R)+ qryA(rc[o],mid+1,r,mid+1,y,L,R);}int qryB(int o,int l,int r,int K,int L,int R){if(l==r)return l;down(o);int tmp=treap::count(cur[rc[o]],L,R);//printf("%d %d %d %d %d\n",l,r,L,R,tmp);if(K<=tmp)return qryB(rc[o],mid+1,r,K,L,R);return qryB(lc[o],l,mid,K-tmp,L,R);}int qryC(int o,int l,int r,int x,int y,int L,int R){if(l==x&&y==r)return treap::getmx(cur[o],L,R);down(o);if(y<=mid)return qryC(lc[o],l,mid,x,y,L,R);if(mid<x)return qryC(rc[o],mid+1,r,x,y,L,R);return max(qryC(lc[o],l,mid,x,mid,L,R),qryC(rc[o],mid+1,r,mid+1,y,L,R));}#undef mid}
维护线段
需要一些分类讨论
/**Treap**/int lc[N],rc[N],sz[N],key[N],L[N],R[N];int len[N],rev[N],lazy[N],rt,size;inline int nw(int l,int r){int o=++size;if(l<=r)L[o]=l,R[o]=r;else L[o]=r,R[o]=l,rev[o]=1;key[o]=rand(),sz[o]=len[o]=abs(r-l)+1;return o;}inline void reverse(int o){if(!o)return;swap(lc[o],rc[o]);lazy[o]^=1,rev[o]^=1;}inline void down(int o){if(lazy[o]){reverse(lc[o]);reverse(rc[o]);lazy[o]=0;}}inline void up(int o){sz[o]=sz[lc[o]]+len[o]+sz[rc[o]];}int make(int x,int y){if(!x||!y)return x|y;down(x),down(y);if(key[x]<key[y]){rc[x]=make(rc[x],y);return up(x),x;}else{lc[y]=make(x,lc[y]);return up(y),y;}}pii splitseg(int x,int k){pii y;if(!rev[x]){y.fi=nw(L[x],L[x]+k-1);y.se=nw(L[x]+k,R[x]);}else{y.fi=nw(R[x],R[x]-k+1);y.se=nw(R[x]-k,L[x]);}return y;}pii split(int x,int k){if(!x)return mp(0,0);if(!k)return mp(0,x);if(k==sz[x])return mp(x,0);down(x);if(!lc[x]&&!rc[x])return splitseg(x,k);pii y;if(k<=sz[lc[x]]){y=split(lc[x],k);lc[x]=y.se,y.se=x;return up(x),y;}else if(k>=len[x]+sz[lc[x]]){y=split(rc[x],k-len[x]-sz[lc[x]]);rc[x]=y.fi,y.fi=x;return up(x),y;}else{int LC=lc[x],RC=rc[x];y=splitseg(x,k-sz[lc[x]]);lc[y.fi]=LC,rc[y.se]=RC;up(y.fi),up(y.se);return y;}}void Mov(int l,int r){pii x=split(rt,l-1);pii y=split(x.se,r-l+1);rt=make(y.fi,make(x.fi,y.se));}void Rev(int l,int r){pii x=split(rt,l-1);//printf("# %d\n",sz[x.fi]);pii y=split(x.se,r-l+1);reverse(y.fi);//printf("! %d %d\n",sz[y.fi],sz[y.se]);rt=make(x.fi,make(y.fi,y.se));//printf("@ %d\n",sz[rt]);}int clk;void print(int o){if(!o)return;down(o);print(lc[o]);if(!rev[o])v.pb(mp(L[o],R[o]));else v.pb(mp(R[o],L[o]));print(rc[o]);}
注意实现
#include<bits/stdc++.h>#define rep(i,a,b) for(int i=(a);i<=(b);i++)#define per(i,a,b) for(int i=(a);i>=(b);i--)#define REP(i,n) for(int i=(0);i<(n);i++)#define fi first#define se second#define pb push_back#define mp make_pairusing namespace std;typedef pair<int,int> pii;typedef vector<int> vi;typedef long long lll;typedef long long ll;inline int read(){char ch=getchar();int x=0,op=0;while(!isdigit(ch))op|=(ch=='-'),ch=getchar();while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return op?-x:x;}const int N=200005;struct P{ll x,y;P(ll _x=0,ll _y=0){x=_x,y=_y;}friend P operator + (const P &a,const P &b){return P(a.x+b.x,a.y+b.y);}friend P operator - (const P &a,const P &b){return P(a.x-b.x,a.y-b.y);}friend lll operator * (const P &a,const P &b){return (lll)a.x*b.y-(lll)a.y*b.x;}inline ll f(const int &k){return k*x+y;}}emp;vector<pair<int,P>> g[N],e[N];vector<P> L,R,all;int n,m;void rebuild(int u,int fa){vector<pair<int,P>> son;for(auto v:g[u])if(v.fi!=fa){rebuild(v.fi,u);son.pb(v);}int last=u,now;for(auto v:son){now=++n;e[last].pb(mp(now,emp));e[now].pb(mp(last,emp));e[now].pb(mp(v.fi,v.se));e[v.fi].pb(mp(now,v.se));last=now;}}void convex(vector<P> &p){vector<P> res;sort(p.begin(),p.end(),[&](const P &a,const P &b){return a.x<b.x||a.x==b.x&&a.y>b.y;});int m=0;REP(i,p.size()){if(i&&p[i].x==p[i-1].x)continue;while(m&&res[m-1].y<=p[i].y)res.pop_back(),m--;while(m>=2&&(p[i]-res[m-2])*(res[m-1]-res[m-2])<=0)res.pop_back(),m--;res.pb(p[i]),m++;}p=res;}void merge(){all.pb(L[0]+R[0]);for(int i=0,j=0;i+1<L.size()||j+1<R.size();)if(j+1==R.size()||(i+1<L.size()&&(L[i+1]-L[i])*(R[j+1]-R[j])<0))all.pb(L[++i]+R[j]);elseall.pb(L[i]+R[++j]);}P bas;int sz[N],vis[N],rt,prt;void find(int u,int fa,int s,P las){sz[u]=1;for(auto v:e[u])if(v.fi!=fa&&!vis[v.fi]){find(v.fi,u,s,v.se);sz[u]+=sz[v.fi];}if(fa&&(!rt||max(sz[u],s-sz[u])<max(sz[rt],s-sz[rt])))rt=u,prt=fa,bas=las;}void get(int u,int fa,vector<P> &p,P now){p.pb(now);for(auto v:e[u])if(v.fi!=fa&&!vis[v.fi])get(v.fi,u,p,now+v.se);}void divide(int x,int s){if(s==1)return;rt=0,find(x,0,s,emp);int u=rt,v=prt;int su=sz[u],sv=s-su;L.clear(),get(u,v,L,emp);R.clear(),get(v,u,R,bas);convex(L),convex(R),merge();vis[v]=1,divide(u,su),vis[v]=0;vis[u]=1,divide(v,sv),vis[v]=0;}int main(){n=read(),m=read();if(n==1){while(m--)puts("0");return 0;}rep(i,1,n-1){int u=read(),v=read();int x=read(),y=read();g[u].pb(mp(v,P(x,y)));g[v].pb(mp(u,P(x,y)));}rebuild(1,0);divide(1,n);convex(all);for(int i=0,j=0;i<m;i++){while(j+1<all.size()&&all[j+1].f(i)>all[j].f(i))j++;printf("%lld%c",all[j].f(i)," \n"[i==m-1]);}return 0;}
May holiday
可以暴力建虚树
#include<bits/stdc++.h>#define rep(i,a,b) for (int i=(a); i<=(b); i++)#define per(i,a,b) for (int i=(a); i>=(b); i--)#define REP(i,n) for (int i=(0); i<(n); i++)#define clr(x) memset(x, 0, sizeof x)using namespace std;const int N = 100005, M = 500;int on[N], c[N], cur[N], ex[N], s[N], f[N];int p[N], t[N], q[N], h[N], pa[N];vector<pair<int,int> > v[N];vector<int> e[N];int n, m, ans;int clk;void dfs(int u, int fa) {h[++clk] = u;for (auto v : e[u])if (v != fa) dfs(v, u);}int main() {scanf("%d%d", &n, &m);rep (i, 2, n) scanf("%d", &p[i]), e[p[i]].push_back(i);dfs(1, 0);rep (i, 1, n) scanf("%d", &t[i]);rep (i, 1, m) scanf("%d", &q[i]);for (int l = 1, r = min(M, m); l <= m; l = r + 1, r = min(r + M, m)) {clr(on), clr(c);rep (i, l, r) on[abs(q[i])] = 1;per (i, n, 1) {int x = h[i];if (c[x] >= 2) on[x] = 1;if (on[x] || c[x]) c[p[x]] ++;}rep (i, 1, n) {int x = h[i];if (on[p[x]]) pa[x] = p[x];else pa[x] = pa[p[x]];}rep (i, 1, n) if (on[i]) {vector<int> tmp;for (int x = p[i]; x && !on[x]; x = p[x])if (!s[x]) tmp.push_back(t[x]);sort(tmp.begin(), tmp.end());for (int l = 0, r = 0; l < tmp.size(); l = r + 1, r = l) {while (r + 1 < tmp.size() && tmp[l] == tmp[r + 1]) r ++;v[i].push_back(make_pair(tmp[l], r - l + 1));}cur[i] = -1; ex[i] = 0;while (cur[i] + 1 < v[i].size() && v[i][cur[i] + 1].first < 0)cur[i] ++;}rep (i, l, r) {int u = abs(q[i]), op = q[i] / u;if (t[u] < 0 && !s[u]) ans --;s[u] += op;if (t[u] < 0 && !s[u]) ans ++;for (int x = u; x; x = pa[x]) {if (x != u) {if (t[x] < 0 && !s[x]) ans --;t[x] -= op;if (t[x] < 0 && !s[x]) ans ++;}ex[x] += op;while (cur[x] + 1 < v[x].size()&& v[x][cur[x] + 1].first - ex[x] < 0)ans += v[x][++cur[x]].second;while (cur[x] >= 0&& v[x][cur[x]].first - ex[x] >= 0)ans -= v[x][cur[x]--].second;}printf("%d ", ans);}clr(f);rep (i, l, r) f[abs(q[i])] += (q[i] > 0 ? 1 : -1);per (i, n, 1) {int x = h[i];f[p[x]] += f[x];}rep (i, 1, n) if (!on[i]) t[i] -= f[i];rep (i, 1, n) if (on[i]) v[i].clear();}return 0;}
把链拆成两条会容易写很多
#include<bits/stdc++.h>#define rep(i,a,b) for(int i=(a);i<=(b);++i)#define per(i,a,b) for(int i=(a);i>=(b);--i)#define REP(i,n) for(int i=(0);i<(n);++i)#define pb push_backusing namespace std;typedef vector<int> vi;inline int read(){char ch=getchar();int x=0,op=0;while(!isdigit(ch))op|=(ch=='-'),ch=getchar();while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return op?-x:x;}const int N=100005,M=502;int in[N],out[N],fa[N],d[N],id[N],rt[N];int f[N][M],g[N][M],h[N][M]; //f 向上跳k步到达的点int n,Q,H,x,y,z; //g 步长为k时,不离开块的步数vi e[N],w[N]; //h 步长为k,到达的位置int clk;inline int isanc(int a,int b){return in[a]<=in[b]&&out[b]<=out[a];}void dfs(int u,int dep=0){in[u]=++clk,id[clk]=u;if(!dep||dep==H-1||dep==H)dep=0,rt[u]=u;REP(i,e[u].size())if(e[u][i]!=fa[u]){d[e[u][i]]=d[u]+w[u][i];fa[e[u][i]]=u;dfs(e[u][i],dep+w[u][i]);}out[u]=clk;}void init(){fa[1]=1,dfs(1,0);rep(i,1,n){int u=id[i];if(!rt[u])rt[u]=rt[fa[u]];}rep(i,1,n){int u=id[i],v=u;rep(j,1,H){if(v>=2&&d[u]-d[fa[v]]<=j)v=fa[v];f[u][j]=v;}}rep(i,1,n){int u=id[i],v;rep(j,1,H){v=f[u][j];if(u==v||rt[v]!=rt[u]){g[u][j]=0,h[u][j]=u;}else{g[u][j]=g[v][j]+1;h[u][j]=h[v][j];}}}}int bf1(int x,int y,int k){int res=0,nxt;for(;k<=H;){if(d[y]>d[x])swap(x,y);nxt=f[x][k];if(isanc(nxt,y))break;res++,x=nxt;}int dist=0;while(x!=y){if(d[y]>d[x])swap(x,y);dist+=d[x]-d[fa[x]],x=fa[x];}return res+1+(dist>k);}int solve1(int x,int y,int k){int res=0,nxt;while(x!=y){if(d[y]>d[x])swap(x,y);nxt=h[x][k];if(isanc(nxt,y))return res+bf1(x,y,k);res+=g[x][k],x=nxt;if(x==y)break;nxt=f[x][k];if(isanc(nxt,y))return res+bf1(x,y,k);res++,x=nxt;}return res;}int jump(int x,int k){while(k&&x>=2){if(x==rt[x]){if(d[x]-d[fa[x]]<=k)k-=d[x]-d[fa[x]],x=fa[x];else return x;}else if(d[x]-d[rt[x]]<=k)k-=d[x]-d[rt[x]],x=rt[x];else return f[x][k];}return x;}int solve2(int x,int y,int k){int res=0,nxt;while(x!=y){if(d[y]>d[x])swap(x,y);nxt=jump(x,k);if(isanc(nxt,y))return res+bf1(x,y,k);res++,x=nxt;}return res;}int main(){n=read();H=max(2,(int)sqrt(n));rep(i,1,n-1){x=read(),y=read(),z=read();e[x].pb(y),w[x].pb(z);e[y].pb(x),w[y].pb(z);}init();Q=read();while(Q--){x=read(),y=read(),z=read();if(z<=H)printf("%d\n",solve1(x,y,z));else printf("%d\n",solve2(x,y,z));}return 0;}
zkw 线段树
能够有效减小常数。适用于单点修改,区间查询的简单信息维护。
namespace seg {const int b = 1<<18; int t[(1<<19)+5];inline void init() { memset(t, 0x3f, sizeof t); }inline void u(int x, int v) {for (t[x+=b]=v, x>>=1; x; x>>=1)t[x] = min(t[x<<1], t[x<<1|1]);}inline int q(int l, int r) {int ans = inf;for (l+=b-1, r+=b+1; l^r^1; l>>=1, r>>=1) {if (~l&1) cmin(ans, t[l^1]);if ( r&1) cmin(ans, t[r^1]);}return ans;}}
减少堆的常数
使用C语言的函数,略快于priority_queue
struct PQ {node a[N * 5]; int w;inline void push(const node &x) {a[++w] = x; push_heap(a + 1, a + w + 1);}inline void pop() {pop_heap(a + 1, a + w + 1); --w;}inline const node& top() {return a[1];}} Q;
李超树
只需要支持插入线段,单点求最值。特别好写
struct L{ll k,b;L(ll _k=0,ll _b=-1e18){k=_k,b=_b;}ll operator () (ll x){return k*x+b;}};struct seg{L x;seg *lc,*rc;seg():x(){lc=rc=0;};void add(L y,ll l=0,ll r=1e9){ll m=(l+r)>>1;if(y(m)>x(m))swap(x,y);if(x(l)<y(l)){if(!lc)lc=new seg;lc->add(y,l,m);}else if(x(r)<y(r)){if(!rc)rc=new seg;rc->add(y,m+1,r);}}ll qry(ll k,ll l=0,ll r=1e9){ll res=x(k),m=(l+r)>>1;if(k<=m&&lc)res=max(res,lc->qry(k,l,m));if(k>m&&rc)res=max(res,rc->qry(k,m+1,r));return res;}}s,t;
数据结构拆分在namespace与struct中
例如树链上维护分块,或者SAM嵌套数据结构。拆分出来是一个很好的选择,减少调试难度。
同时可以结合指针动态开辟。
struct block{int *pre,*suf,sz,L,R,ans,tag;pii *a,*b,*c;inline void doit(int flag){if(flag){pre[1]=0,suf[0]=1;pre[sz+1]=sz,suf[sz]=sz+1;ans=1;rep(i,2,sz)if(a[i].fi!=a[i-1].fi)pre[i]=i-1;else pre[i]=pre[i-1];per(i,sz-1,1)if(a[i].fi!=a[i+1].fi)suf[i]=i+1;else suf[i]=suf[i+1];}while(ans<=sz&&a[ans].fi+tag<=0)ans=suf[ans];while(pre[ans]&&a[pre[ans]].fi+tag>0)ans=pre[pre[ans]]+1;}void build(int l,int r){L=l,R=r,sz=r-l+1;a=new pii[sz+3];b=new pii[sz+3];c=new pii[sz+3];pre=new int[sz+3];suf=new int[sz+3];rep(i,1,sz)a[i]=mp(w[id[l-1+i]],l-1+i);sort(a+1,a+sz+1);tag=ans=0,doit(1);}void upd(int l,int r,int ex){if(l==L&&r==R)return tag+=ex,doit(0);int n=0,m=0,p=1,q=1,owo=1;rep(i,1,sz)if(l<=a[i].se&&a[i].se<=r)b[++n]=mp(a[i].fi+ex+tag,a[i].se);elsec[++m]=mp(a[i].fi+tag,a[i].se);while(p<=n||q<=m)if(p<=n&&(q>m||b[p]<c[q]))a[owo++]=b[p++];else a[owo++]=c[q++];tag=ans=0,doit(1);}int qry(int l,int r){if(l==L&&r==R)return sz-ans+1;int res=0;rep(i,1,sz)if(l<=a[i].se&&a[i].se<=r)res+=((a[i].fi+tag)>0);return res;}}b[N];
ST表支持O(1)回答LCA
namespace Dis {vector<pii> e[maxn];int d[maxn], id[maxn], f[maxn<<1][lg], Lg[maxn<<1], n, clk;inline void init(int _n) { n = _n; }inline void link(int a, int b, int c) {e[a].pb(mp(b, c));e[b].pb(mp(a, c));}void dfs(int u, int fa) {f[id[u] = ++clk][0] = d[u];REP (i, e[u].size())if (e[u][i].fi != fa) {d[e[u][i].fi] = d[u] + e[u][i].se;dfs(e[u][i].fi, u);f[++clk][0] = d[u];}}void prepare() {memset(f, 0x3f, sizeof f);dfs(1, 0); Lg[0] = -1;rep (i, 1, clk) Lg[i] = Lg[i>>1] + 1;rep (i, 1, 18) rep (j, 1, clk - (1 << i))f[j][i] = min(f[j][i-1], f[j+(1<<(i-1))][i-1]);}inline int ask(int a, int b) {static int t, res; res = d[a] + d[b];a = id[a]; b = id[b];if (a > b) swap(a, b); t = Lg[b-a+1];return res - 2 * min(f[a][t], f[b-(1<<t)+1][t]);}}
void init(int lim){rep(i,2,lim){if(!flag[i])p[++cnt]=i,sp[cnt]=(sp[cnt-1]+i)%mod;for (int j=1;j<=cnt&&i*p[j]<=lim;++j){flag[i*p[j]]=1;if(i%p[j]==0)break;}}}inline int id(ll x){return x<=T?b[x]:c[n/x];}void G(){for (ll last=1,i;last<=n;last=i+1){i=n/(n/last);a[++m]=n/i;if(a[m]<=T)b[a[m]]=m;else c[n/a[m]]=m;h[m]=(a[m]-1)%mod;g[m]=1ll*h[m]*(h[m]+3)%mod;if(g[m]&1)g[m]+=mod;g[m]>>=1;}rep(j,1,cnt)for(int i=1;i<=m&&a[i]>=1ll*p[j]*p[j];++i){h[i]=(h[i]-(h[id(a[i]/p[j])]-(j-1)+mod)%mod+mod)%mod;g[i]=(g[i]-1ll*p[j]*(g[id(a[i]/p[j])]-sp[j-1]+mod)%mod+mod)%mod;}}int S(ll n,int k){if(n<=1||p[k]>n)return 0;int t=id(n);ll res=(ll)g[t]-h[t]-(sp[k-1]-(k-1));if(k==1)res+=2;for(int i=k;i<=cnt&&1ll*p[i]*p[i]<=n;++i){ll p1=p[i],p2=p1*p1;for(int e=1;p2<=n;++e,p1=p2,p2*=p[i])res+=(1ll*(p[i]^e)*S(n/p1,i+1)+(p[i]^(e+1)))%mod;}res%=mod;//printf("%lld %d %d\n", n, k, res);return res<0?res+mod:res;}
const int N=1005,M=100005,inf=1e9;struct edge{int u,v,f,c;};struct ISAP{int n,m,s,t;ll res;int num[N],cur[N],d[N];int link[M],h[N],p[N];edge e[M];inline void init(int _n){n=_n,m=0,res=0;memset(h,-1,sizeof h);memset(cur,-1,sizeof cur);}inline void add(int u,int v,int w){e[m]=(edge){u,v,0,w};link[m]=h[u],cur[u]=h[u]=m++;e[m]=(edge){v,u,0,0};link[m]=h[v],cur[v]=h[v]=m++;}inline int aug(){int mn=inf;for(int x=t;x!=s;x=e[p[x]].u)mn=min(mn,e[p[x]].c-e[p[x]].f);for(int x=t;x!=s;x=e[p[x]].u)e[p[x]].f+=mn,e[p[x]^1].f-=mn;return mn;}int Maxflow(int _s,int _t){int u=_s;s=_s,t=_t;memset(num,0,sizeof num);memset(d,0,sizeof d),num[0]=n;while(d[s]<n){if(u==t)res+=aug(),u=s;int flag=0;loop(k,cur[u])if(e[k].f<e[k].c&&d[u]==d[e[k].v]+1){cur[u]=k,p[u=e[k].v]=k;flag=1;break;}if(flag)continue;int mn=n;loop(k,cur[u]=h[u])if(e[k].f<e[k].c)mn=min(mn,d[e[k].v]+1);if(!(--num[d[u]]))return res;num[d[u]=mn]++,u=(u==s?s:e[p[u]].u);}return res;}}G;
背模板!!
#include<bits/stdc++.h>#define rep(i,a,b) for(int i=(a);i<=(b);i++)#define per(i,a,b) for(int i=(a);i>=(b);i--)#define REP(i,n) for(int i=(0);i<(n);i++)using namespace std;const int N=405,inf=1e9+99;int w[N][N],ex[N],ey[N],mn[N];int vx[N],vy[N],cur[N],p[N];int n,m,K,a,b,c;int dfs(int x){vx[x]=1;rep(y,1,n)if(!vy[y]){int d=ex[x]+ey[y]-w[x][y];if(!d){vy[y]=1;if(!cur[y]||dfs(cur[y]))return cur[y]=x,1;}else mn[y]=min(mn[y],d);}return 0;}long long KM(){rep(x,1,n)rep(y,1,n)ex[x]=max(ex[x],w[x][y]);rep(x,1,n){rep(y,1,n)mn[y]=inf;memset(vx,0,sizeof vx);memset(vy,0,sizeof vy);if(dfs(x))continue;for(;;){int d=inf,t=0;rep(y,1,n)if(!vy[y])d=min(d,mn[y]);rep(x,1,n)if(vx[x])ex[x]-=d;rep(y,1,n)if(vy[y])ey[y]+=d;else if(!(mn[y]-=d))t=y;if(!cur[t])break;int e=cur[t];vx[e]=vy[t]=1;rep(y,1,n)mn[y]=min(mn[y],ex[e]+ey[y]-w[e][y]);}memset(vx,0,sizeof vx);memset(vy,0,sizeof vy);dfs(x);}long long ans=0;rep(y,1,n)ans+=w[cur[y]][y],p[cur[y]]=y;return ans;}int main(){scanf("%d%d%d",&n,&m,&K);swap(n,m),n=max(n,m);rep(i,1,K){scanf("%d%d%d",&a,&b,&c);w[a][b]=c;}cout<<KM()<<endl;rep(i,1,m)if(w[i][p[i]]>0)printf("%d ",p[i]);else putchar('0'),putchar(' ');return 0;}
由于稠密图访问的连续性,用vector可能会快很多
const int N=10005,M=300005,inf=1e9+99;namespace G{ //注意只支持非负边权,否则需要spfa预处理#define real d[u]+f[u]-f[t.v]struct edge{int u,v,w,f,c,p;};vector<edge>e[N];int d[N],f[N],s,t,n,m;inline void init(int _n,int _s=0,int _t=0){n=_n,s=(_s?_s:1),t=(_t?_t:n),m=0;}inline void add(int u,int v,int c,int w){e[u].pb((edge){u,v,w,0,c,(int)e[v].size()});e[v].pb((edge){v,u,-w,0,0,(int)e[u].size()-1});}struct node{int d,v;friend bool operator < (const node &a,const node &b){return a.d>b.d;}};priority_queue<node>Q;bool dij(){rep(i,1,n){f[i]=min(f[i]+d[i],inf);d[i]=i==s?0:inf;}Q.push((node){0,s});while(!Q.empty()){int t=Q.top().d,u=Q.top().v;Q.pop();if(t>d[u])continue;REP(k,e[u].size()){edge &t=e[u][k];if(t.f<t.c&&real+t.w<d[t.v])Q.push((node){d[t.v]=real+t.w,t.v});}}return d[t]<inf;}int r[N],in[N];int dfs(int u,int a){if(u==t)return a;int res=0;in[u]=1;for(int &k=r[u];k<e[u].size();k++){edge &t=e[u][k];if(t.f<t.c&&real+t.w==d[t.v]&&!in[t.v]){int now=dfs(t.v,min(a,t.c-t.f));t.f+=now,e[t.v][t.p].f-=now,res+=now;if(!(a-=now))break;}}return in[u]=0,res;}void mcmf(int &flow,int &cost){int now;while(dij()){rep(i,1,n)r[i]=0;flow+=(now=dfs(s,inf));cost+=now*(d[t]+f[t]);}}}
用途:构造后缀树,后缀树上可以DP,LCT维护..
NOI 2015 品酒大会
namespace sam{int t[N][26],fa[N],buf[N],mx[N],pos[N],a[N],tail=1,size=1;ll sz[N],f[N],g[N],cnt[N],ans[N];vi e[N],w[N];int nw(int val){return mx[++size]=val,f[size]=-inf,g[size]=inf,size;}void ins(int c,int id,int val){int p=tail,np=nw(mx[p]+1);pos[id]=np,f[np]=g[np]=val,sz[np]=1;for(;!t[p][c];p=fa[p])t[p][c]=np;if(!p)return(void)(fa[np]=1,tail=np);int q=t[p][c];if(mx[q]==mx[p]+1)fa[np]=q;else{int nq=nw(mx[p]+1);fa[nq]=fa[q],fa[q]=fa[np]=nq;REP(k,26)t[nq][k]=t[q][k];for(;p&&t[p][c]==q;p=fa[p])t[p][c]=nq;}tail=np;}void init(){f[1]=-inf,g[1]=inf;rep(i,1,size)buf[mx[i]]++;rep(i,1,size)buf[i]+=buf[i-1];rep(i,1,size)a[buf[mx[i]]--]=i;rep(i,2,size){int u=a[i];e[fa[u]].pb(u);w[fa[u]].pb(mx[u]-mx[fa[u]]);}}void dfs(int u,int d){REP(i,e[u].size()){dfs(e[u][i],d+w[u][i]);cnt[d]+=1ll*sz[u]*sz[e[u][i]];sz[u]+=sz[e[u][i]];if(f[u]>-inf)ans[d]=max(ans[d],1ll*f[u]*f[e[u][i]]);f[u]=max(f[u],f[e[u][i]]);if(g[u]<inf)ans[d]=max(ans[d],1ll*g[u]*g[e[u][i]]);g[u]=min(g[u],g[e[u][i]]);}}void solve(int n){rep(i,0,n)ans[i]=-1ll*inf*inf;dfs(1,0);per(i,n-1,0){cnt[i]+=cnt[i+1];ans[i]=max(ans[i],ans[i+1]);}rep(i,0,n-1)printf("%lld %lld\n",cnt[i],!cnt[i]?0:ans[i]);}}
SAM与dus on tree
绝佳搭档~
const int N=1200005;namespace tool{int c[N],S[N],len,tot;set<int>::iterator i,j;set<int> s;#define lowbit(x) (x&-x)inline void update(int c[],int x,int y){for(;x<=len;x+=lowbit(x))c[x]+=y;}inline int query(int c[],int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}int qry(int k){return k*(tot-query(c,k))+query(S,k);}void upd(int l,int op){l=(l+len)%len;if(l==0)l=len;update(c,l,op),tot+=op;update(S,l,op*l);}void add(int p,int op){if(op==1)s.insert(p);else s.erase(p);if(s.empty()||op==1&&s.size()==1)return upd(0,op);j=s.upper_bound(p);if(j==s.end())j=s.begin();i=s.lower_bound(p);if(i==s.begin())i=s.end();--i;upd(*j-*i,-op);upd(*j-p,op),upd(p-*i,op);}}int ans[N];namespace sam{int t[N][26],fa[N],mx[N],owo[N];int sz[N],sn[N],f[N][24];int size=1,tail=1,clk;vector<pii> q[N];vi e[N];int id[N];void init(){tail=1;}inline int nw(int x,int y=0){return mx[++size]=x,owo[size]=y,size;}int ins(int c,int val=0){int p=tail,np=nw(mx[p]+1,val);for(;p&&!t[p][c];p=fa[p])t[p][c]=np;if(!p)return fa[np]=1,tail=np;int q=t[p][c];if(mx[q]==mx[p]+1)fa[np]=q;else{int nq=nw(mx[p]+1);fa[nq]=fa[q],fa[q]=fa[np]=nq;REP(k,26)t[nq][k]=t[q][k];for(;p&&t[p][c]==q;p=fa[p])t[p][c]=nq;}return tail=np;}void predfs(int u){sz[u]=1,id[++clk]=u;rep(i,1,23)f[u][i]=f[f[u][i-1]][i-1];for(auto v:e[u]){f[v][0]=u,predfs(v),sz[u]+=sz[v];if(sz[v]>sz[sn[u]])sn[u]=v;}}void build(){rep(i,2,size)e[fa[i]].pb(i);predfs(1);}void add(int len,int p,int k,int id){per(i,23,0)if(mx[f[p][i]]>=len)p=f[p][i];q[p].pb(mp(k,id));}void doit(int u,int ty){if(owo[u]&&ty)tool::add(owo[u],1);if(owo[u]&&!ty)tool::add(owo[u],-1);for(auto v:e[u])doit(v,ty);}void solve(int u){for(auto v:e[u])if(v!=sn[u])solve(v),doit(v,0);if(sn[u])solve(sn[u]);for(auto v:e[u])if(v!=sn[u])doit(v,1);if(owo[u])tool::add(owo[u],1);for(auto p:q[u])ans[p.se]=tool::qry(p.fi);}}
用途:快速比较LCP,经常配合ST表使用。几乎都可以被SAM代替。
NOI2016 优秀的拆分
求所有形如 的子串拆分数。
注意:多组数据,数组及时清空。
#include<bits/stdc++.h>#define rep(i,a,b) for (int i=(a); i<=(b); i++)#define per(i,a,b) for (int i=(a); i>=(b); i--)#define clr(x) memset(x, 0, sizeof x)using namespace std;const int maxn = 60005;int SA[maxn], rank[maxn], height[maxn], tmp[maxn], tax[maxn];int pre[maxn], suf[maxn], f[maxn][20], lg[maxn], n, m, l, r;char s[maxn]; long long ans;inline bool cmp(int a[maxn], int x, int y, int w) {return a[x] == a[y] && a[x+w] == a[y+w];}void Sort(int n, int m) {rep (i, 0, m) tax[i] = 0;rep (i, 1, n) tax[rank[i]]++;rep (i, 1, m) tax[i] += tax[i-1];per (i, n, 1) SA[tax[rank[tmp[i]]]--] = tmp[i];}void SuffixArray(char s[maxn], int n) {clr(height); clr(rank); clr(tmp); clr(SA);rep (i, 1, n) { rank[i] = s[i]; tmp[i] = i; }m = 127; Sort(n, m);for (int w=1,p=0,i; w<n; w+=w,m=p,p=0) {rep (i, n-w+1, n) tmp[++p] = i;rep (i, 1, n) if (SA[i] > w) tmp[++p] = SA[i] - w;Sort(n, m); swap(tmp, rank); rank[SA[1]] = p = 1;rep (i, 2, n) rank[SA[i]] = (cmp(tmp, SA[i-1], SA[i], w) ? p : ++p);}for (int i=1,j,k=0; i<=n; height[rank[i++]]=k)for (k=k?k-1:k,j=SA[rank[i]-1]; s[i+k]==s[j+k]; k++);}void pre_ST(int n, int a[maxn]) {clr(f); lg[1] = 0;rep (i, 2, n) lg[i] = lg[i>>1] + 1;rep (i, 1, n) f[i][0] = a[i];rep (j, 1, 18) rep (i, 1, n)if (i+(1<<(j-1)) > n) f[i][j] = f[i][j-1];else f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]);}inline int query(int l, int r) {int t = lg[r - l + 1];return min(f[l][t], f[r-(1<<t)+1][t]);}inline int getLCP(int x, int y) {if (rank[x] > rank[y]) swap(x, y);return query(rank[x]+1, rank[y]);}inline int revLCP(int x, int y) {x = 2*n+2-x; y = 2*n+2-y;if (rank[x] > rank[y]) swap(x, y);return query(rank[x]+1, rank[y]);}int main() {int T; scanf("%d", &T);while (T--) {clr(s); scanf("%s", s + 1); n = strlen(s + 1);rep (i, 1, n) s[2*n+2-i] = s[i]; s[n+1] = '#';SuffixArray(s, 2*n+1); pre_ST(2*n+1, height);clr(pre); clr(suf);rep (L, 1, n) rep (p, 1, n/L-1) {l = min(revLCP(p*L, p*L+L), L);r = min(getLCP(p*L, p*L+L), L);if (r+l-1 < L) continue;pre[p*L+L-l+1+L-1]++; pre[p*L+L+r]--;suf[p*L-l+1]++; suf[p*L+r-1-L+1+1]--;}ans = 0;rep (i, 1, n) {pre[i] += pre[i-1]; suf[i+1] += suf[i];ans += 1LL * pre[i] * suf[i+1];}printf("%lld\n", ans);}return 0;}
注意链上可能需要特判LCA
void tj(int u, int f) {dfn[u] = low[u] = ++c; st[++tp] = u;for (int k=G.h[u]; k; k=G.l[k]) if (G.v[k] != f)if (!dfn[G.v[k]]) {tj(G.v[k], u);cmin(low[u], low[G.v[k]]);if (low[G.v[k]] >= dfn[u]) {w[++t] = inf; T.A(u, t);do T.A(st[tp], t); while (st[tp--] != G.v[k]);}}else cmin(low[u], dfn[G.v[k]]);}
又短又好写
#include<bits/stdc++.h>#define rep(i,a,b) for(int i=(a);i<=(b);i++)#define per(i,a,b) for(int i=(a);i>=(b);i--)#define REP(i,n) for(int i=(0);i<(n);i++)#define pb push_backusing namespace std;typedef vector<int> vi;const int N=505;int cur[N],vis[N],r[N],tim,n,m,ans,a,b;vi e[N];int dfs(int x){if(!x)return 1;vis[x]=tim;vi::iterator it=e[x].begin();vi::iterator ti=e[x].end();random_shuffle(it,ti);for(int u,v;it!=ti;it++){u=*it,v=cur[u];if(vis[v]<tim){cur[x]=u,cur[u]=x,cur[v]=0;if(dfs(v))return 1;cur[u]=v,cur[v]=u,cur[x]=0;}}return 0;}int main(){scanf("%d%d",&n,&m);while(m--){scanf("%d%d",&a,&b);e[a].pb(b),e[b].pb(a);}srand(time(NULL) ^ (long int)(new char));rep(i,1,n)r[i]=i;rep(T,1,3){random_shuffle(r+1,r+n+1);rep(i,1,n)if(!cur[r[i]])tim++,ans+=dfs(r[i]);}printf("%d\n",ans);rep(i,1,n)printf("%d ",cur[i]);return 0;}