练习一下无脑的水分方法。
树链剖分。
平时用来求LCA还是好用的。
T1 RG
分析
求LCA,然后下标为颜色建树,线段树合并
树链剖分只是拿来求LCA的
结果内存不够,
这个时候应该用内存池来存那些无用节点然后再拿出来用。
所以就可以写了。
高端卡内存技巧
复杂度为
O((n+m)logn)
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 100004
#define S 40
#define pb push_back
#define SZ(a) ((int)(a).size())
int n,m,q;
vector<int>P[M];
int Lp[M*S],Rp[M*S],Mx[M*S],Cnt[M*S],trp;
int Stk[M*S],tp;//内存池
#define lson l,mid,Lp[p]
#define rson mid+1,r,Rp[p]
void Up(int p){
if(Cnt[Rp[p]]>Cnt[Lp[p]])Cnt[p]=Cnt[Rp[p]],Mx[p]=Mx[Rp[p]];
else Cnt[p]=Cnt[Lp[p]],Mx[p]=Mx[Lp[p]];
}
void Insert(int l,int r,int &p,int old,int x){
if(tp) p=Stk[tp--];//使用旧节点
else p=++trp;
if(l==r){
Mx[p]=l;
Cnt[p]=Cnt[old]+(x<0?-1:1);
return;
}
int mid=l+r>>1;
if(abs(x)<=mid)Rp[p]=Rp[old],Insert(lson,Lp[old],x);
else Lp[p]=Lp[old],Insert(rson,Rp[old],x);
Up(p);
if(old)Stk[++tp]=old;//回收旧节点
}
int Merge(int l,int r,int p,int q){
if(!p || !q)return p|q;
Stk[++tp]=q;//回收旧节点
if(l==r){
Cnt[p]+=Cnt[q];
return p;
}
int mid=l+r>>1;
Lp[p]=Merge(lson,Lp[q]);
Rp[p]=Merge(rson,Rp[q]);
Up(p);
return p;
}
int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int Sz[M],Son[M],Top[M],Fa[M],Dep[M];
void DFS(int A,int f){
Sz[A]=1;
Dep[A]=Dep[f]+1;
Fa[A]=f;
Son[A]=0;
int B;
LREP(i,A)if((B=V[i])!=f){
DFS(B,A);
Sz[A]+=Sz[B];
if(Sz[B]>Sz[Son[A]])Son[A]=B;
}
}
void FindTop(int A,int tp){
Top[A]=tp;
if(Son[A])FindTop(Son[A],tp);
int B;
LREP(i,A)if((B=V[i])!=Fa[A] && B!=Son[A])
FindTop(B,B);
}
int LCA(int A,int B){
while(Top[A]!=Top[B])
if(Dep[Top[A]]>Dep[Top[B]])A=Fa[Top[A]];
else B=Fa[Top[B]];
return Dep[A]<Dep[B]?A:B;
}
int Ans[M],Rt[M];
void Answer(int A){
int B;
if(tp)Rt[A]=Stk[tp--];
else Rt[A]=++trp;
Mx[Rt[A]]=Cnt[Rt[A]]=Lp[Rt[A]]=Rp[Rt[A]]=0;
LREP(i,A)if((B=V[i])!=Fa[A])
Answer(B),Rt[A]=Merge(1,m,Rt[A],Rt[B]);
REP(i,0,SZ(P[A]))
Insert(1,m,Rt[A],Rt[A],P[A][i]);
Ans[A]=Mx[Rt[A]];
}
int main(){
while(scanf("%d%d",&n,&q)!=EOF){
if(!n)break;
memset(Head,tot=tp=trp=0,sizeof(Head));
REP(i,0,n+1)vector<int>().swap(P[i]);
REP(i,1,n){
int u,v;
Rd(u),Rd(v);
Add_Edge(u,v);
Add_Edge(v,u);
}
DFS(1,0);
FindTop(1,1);
m=0;
while(q--){
int u,v,z,c;
Rd(u),Rd(v),Rd(z);
c=LCA(u,v);
P[u].pb(z);
P[v].pb(z);
P[c].pb(-z);
P[Fa[c]].pb(-z);
chkmax(m,z);
}
Answer(1);
REP(i,1,n+1)printf("%d\n",Ans[i]);
}
return 0;
}
T2 TWNMZ
分析
十分玄学的一道题。
正常写法是树链剖分然后分块维护。
不过玄学写法更快(?)
这份代码采用的是单调栈暴力加直接暴力。
用了vector跑了4s。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 100004
#define MS 20
int n,m,q,T,Val[M];
int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int Sz[M],Son[M],Top[M],Fa[M],Dep[M];
void DFS(int A,int f){
Sz[A]=1;
Dep[A]=Dep[f]+1;
Fa[A]=f;
Son[A]=0;
int B;
LREP(i,A)if((B=V[i])!=f){
DFS(B,A);
Sz[A]+=Sz[B];
if(Sz[B]>Sz[Son[A]])Son[A]=B;
}
}
void FindTop(int A,int tp){
Top[A]=tp;
if(Son[A])FindTop(Son[A],tp);
int B;
LREP(i,A)if((B=V[i])!=Fa[A] && B!=Son[A])
FindTop(B,B);
}
int LCA(int A,int B){
while(Top[A]!=Top[B])
if(Dep[Top[A]]>Dep[Top[B]])A=Fa[Top[A]];
else B=Fa[Top[B]];
return Dep[A]<Dep[B]?A:B;
}
struct Question{
int u,v,k,id;
bool operator <(const Question &_)const{
return k<_.k;
}
}Qes[M];
#define pb push_back
#define SZ(a) ((int)(a).size())
struct Node{int Lim,elen,k,id;};
vector<Node>Q[M];
bool Cmp(int a,int b){return Val[a]>Val[b];}
bool Cmp2(int a,int b){return Dep[a]<Dep[b];}
int Ans[M],Nowk;
int Stk[MS][M],Tp[MS];
int Use[M],tp;
void Answer(int A){
Use[++tp]=A;
int p=Dep[A]%Nowk,w=lower_bound(Stk[p],Stk[p]+Tp[p],A,Cmp)-Stk[p];
int Tmp=Stk[p][w],Tmpl=Tp[p],B;
Stk[p][w]=A,Tp[p]=w+1;
LREP(i,A)if((B=V[i])!=Fa[A])
Answer(B);
REP(i,0,SZ(Q[A])){
Node TQ=Q[A][i];
if(!TQ.elen){
int wt=lower_bound(Stk[p],Stk[p]+Tp[p],TQ.Lim,Cmp2)-Stk[p];
chkmax(Ans[TQ.id],Val[Stk[p][wt]]);
}
else if(Dep[A]-TQ.elen>=Dep[TQ.Lim]){
int re=tp-TQ.elen;TQ.elen=0;
Q[Use[re]].pb(TQ);
}
}
vector<Node>().swap(Q[A]);
Stk[p][w]=Tmp,Tp[p]=Tmpl;
tp--;
}
void Find(int A){
int B;
Use[++tp]=A;
LREP(i,A)if((B=V[i])!=Fa[A])
Find(B);
REP(i,0,SZ(Q[A])){
Node TQ=Q[A][i];
if(!TQ.elen){
chkmax(Ans[TQ.id],Val[A]);
if(Dep[A]-TQ.k>=Dep[TQ.Lim])Q[Use[tp-TQ.k]].pb(TQ);
}
else if(Dep[A]-TQ.elen>=Dep[TQ.Lim]){
int re=tp-TQ.elen;TQ.elen=0;
Q[Use[re]].pb(TQ);
}
}
vector<Node>().swap(Q[A]);
tp--;
}
int main(){
Rd(T);
REP(Case,1,T+1){
memset(Head,tot=0,sizeof(Head));
Rd(n),Rd(q);
REP(i,1,n+1)Rd(Val[i]);
REP(i,1,n){
int u,v;
Rd(u),Rd(v);
Add_Edge(u,v);
Add_Edge(v,u);
}
DFS(1,0);
FindTop(1,1);
REP(i,0,q)Rd(Qes[i].u),Rd(Qes[i].v),Rd(Qes[i].k),Qes[i].id=i;
memset(Ans,0,sizeof(Ans));
sort(Qes,Qes+q);
int i=0;
while(i<q){
if(Qes[i].k>=MS)break;
Nowk=Qes[i].k;
while(i<q && Qes[i].k==Nowk){
int u=Qes[i].u,v=Qes[i].v,c=LCA(u,v),d=(Dep[u]+Dep[v]-(Dep[c]<<1)+1)%Qes[i].k;
Q[u].pb((Node){c,Nowk-1,Qes[i].k,Qes[i].id});
Q[v].pb((Node){c,d,Qes[i].k,Qes[i].id});
i++;
}
Answer(1);
}
if(i!=q){
while(i<q){
int u=Qes[i].u,v=Qes[i].v,c=LCA(u,v),d=(Dep[u]+Dep[v]-(Dep[c]<<1)+1)%Qes[i].k;
Q[u].pb((Node){c,Qes[i].k-1,Qes[i].k,Qes[i].id});
Q[v].pb((Node){c,d,Qes[i].k,Qes[i].id});
i++;
}
Find(1);
}
printf("Case #%d:\n",Case);
REP(j,0,q)printf("%d\n",Ans[j]);
}
return 0;
}
T3 CSG
这个显然是主席树裸题
树链剖分的写法:
重排序权值一维作为时间维,然后直接树链剖分求和。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 100004
#define S 20
int n,m,q,Val[M],P[M];
int Lp[M*S],Rp[M*S],Rt[M],trp;
LL Sum[M*S];
int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int Sz[M],Son[M],Top[M],Fa[M],Dep[M];
#define lson l,mid,Lp[p]
#define rson mid+1,r,Rp[p]
void Insert(int l,int r,int &p,int old,int t){
p=++trp;
if(l==r){
Sum[p]=Sum[old]+P[l];
return;
}
int mid=l+r>>1;
if(t<=mid)Rp[p]=Rp[old],Insert(lson,Lp[old],t);
else Lp[p]=Lp[old],Insert(rson,Rp[old],t);
Sum[p]=Sum[Lp[p]]+Sum[Rp[p]];
}
LL Query(int l,int r,int p,int a,int b){
if(!p || l>b || r<a)return 0;
if(a<=l && r<=b)return Sum[p];
int mid=l+r>>1;
return Query(lson,a,b)+Query(rson,a,b);
}
void DFS(int A,int f){
Insert(1,m,Rt[A],Rt[f],Val[A]);
Sz[A]=1;
Dep[A]=Dep[f]+1;
Fa[A]=f;
Son[A]=0;
int B;
LREP(i,A)if((B=V[i])!=f){
DFS(B,A);
Sz[A]+=Sz[B];
if(Sz[B]>Sz[Son[A]])Son[A]=B;
}
}
void FindTop(int A,int tp){
Top[A]=tp;
if(Son[A])FindTop(Son[A],tp);
int B;
LREP(i,A)if((B=V[i])!=Fa[A] && B!=Son[A])
FindTop(B,B);
}
int LCA(int A,int B){
while(Top[A]!=Top[B])
if(Dep[Top[A]]>Dep[Top[B]])A=Fa[Top[A]];
else B=Fa[Top[B]];
return Dep[A]<Dep[B]?A:B;
}
int main(){
while(scanf("%d",&n)!=EOF){
memset(Head,tot=trp=0,sizeof(Head));
Rd(q);
REP(i,1,n+1)Rd(Val[i]),P[i]=Val[i];
sort(P+1,P+n+1);
m=unique(P+1,P+n+1)-P-1;
REP(i,1,n+1)Val[i]=lower_bound(P+1,P+m+1,Val[i])-P;
REP(i,1,n){
int u,v;
Rd(u),Rd(v);
Add_Edge(u,v);
Add_Edge(v,u);
}
DFS(1,0);
FindTop(1,1);
while(q--){
int x,y,c,a,b;
Rd(x),Rd(y),Rd(a),Rd(b);
a=lower_bound(P+1,P+m+1,a)-P,b=upper_bound(P+1,P+m+1,b)-P-1;
c=LCA(x,y);
printf("%lld",Query(1,m,Rt[x],a,b)+Query(1,m,Rt[y],a,b)-Query(1,m,Rt[c],a,b)-Query(1,m,Rt[Fa[c]],a,b));
if(q)putchar(' ');
}
puts("");
}
return 0;
}