九下五月上旬日记
5.1
闲话
- \(miaomiao\) 上午 \(7:50 \sim 12:10\) 安排了一场模拟赛。
- 详见 2024 HE中考 游记 5.1 。
做题纪要
luogu P3521 [POI2011] ROT-Tree Rotations
-
What is 先序遍历 ?
-
对于一棵子树的根节点 \(x\) ,交换其左右子树,不会对以前的节点产生影响(不会更改贡献)。
-
这棵子树产生的贡献值来自三种情况,分别是都在左子树,都在右子树,左子树和右子树各一个。只考虑第三种情况的求解,前两种可以递归处理。
-
对于每个叶子节点建立权值线段树并依次向上合并。
- 动态开点权值线段树加线段树合并板子。
- 设两棵线段树分别为 \(A,B\) ,从根节点开始递归合并。
- 递归到非叶子节点时,如果 \(A\) 树或 \(B\) 树上的对应节点为空,则直接返回另一个树上对应节点;递归到叶子节点时,直接合并(要符合区间可加性);否则,选一个节点作为合并之后的点,用另一个点来更新信息。
- 一般情况下为节省空间,不再新开线段树而直接在其中一棵线段树上修改。
- 时空复杂度为 \(O(m \log_{2} V)\) ,其中 \(V\) 是值域,一般情况有 \(n,m\) 同阶,故常写作 \(O(n \log_{2} V)\) 。
- 动态开点权值线段树加线段树合并板子。
-
在权值线段树合并过程中,由于 \(rt_{1},rt_{2}\) 所代表的区间是同一个区间,故 \(rt_{1}\) 的右子树一定比 \(rt_{2}\) 的左子树大。此时不交换其左右子树产生的贡献为 \(sum_{rson_{rt_{1}}} \times sum_{lson_{rt_{2}}}\) ,交换其左右子树产生的贡献为 \(sum_{lson_{rt_{1}}} \times sum_{rson_{rt_{2}}}\) 。最终两者取 \(\min\) 即可。
-
LibreOJ 2163. 「POI2011 R2 Day2」旋转树木 Tree Rotations 上时限开了 \(160ms\) ,加个快读就行了。
点击查看代码
const ll Maxn=3e5+10,Maxnlogn=Maxn*(log2(Maxn)+5); ll rt_sum=0,ans=0,sum1=0,sum2=0; struct SegmentTree { ll ls,rs,sum; }tree[Maxnlogn]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } ll build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(ll rt,ll l,ll r,ll pos,ll val) { if(l==r) { tree[rt].sum+=val; return; } ll mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val); } pushup(rt); } ll merge(ll rt1,ll rt2,ll l,ll r) { if(rt1==0)//如果有节点为空返回另一个节点 { return rt2; } if(rt2==0) { return rt1; } if(l==r)//找到叶子节点合并并返回 { tree[rt1].sum+=tree[rt2].sum; return rt1; } sum1+=tree[rson(rt1)].sum*tree[lson(rt2)].sum; sum2+=tree[lson(rt1)].sum*tree[rson(rt2)].sum; ll mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } ll dfs(ll n) { ll root,ls,rs,x; x=read(); if(x==0) { ls=dfs(n); rs=dfs(n); sum1=sum2=0; root=merge(ls,rs,1,n);//合并 ans+=min(sum1,sum2); } else { root=build(); update(root,1,n,x,1); } return root; } int main() { ll n; n=read(); dfs(n); write(ans); return 0; }
luogu P3605 [USACO17JAN] Promotion Counting P
-
离散化后自下而上线段树合并即可。
点击查看代码
const int Maxn=1e5+10,Maxnlogn=Maxn*(log2(Maxn)+5); struct node { int nxt,to; }e[Maxn]; int head[Maxn],p[Maxn],b[Maxn],root[Maxn],ans[Maxn],cnt=0,rt_sum=0; struct SegmentTree { int ls,rs,sum; }tree[Maxnlogn]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } int build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].sum+=val; return; } int mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val); } pushup(rt); } int merge(int rt1,int rt2,int l,int r) { if(rt1==0) { return rt2; } if(rt2==0) { return rt1; } if(l==r) { tree[rt1].sum+=tree[rt2].sum; return rt1; } int mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } int query(int rt,int l,int r,int x,int y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int n) { for(int i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to,n); root[x]=merge(root[x],root[e[i].to],1,n); } ans[x]=query(root[x],1,n,p[x]+1,n); } int main() { int n,u,v,i; cin>>n; for(i=1;i<=n;i++) { cin>>p[i]; b[i]=p[i]; } for(i=2;i<=n;i++) { cin>>u; v=i; add(u,v); } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { p[i]=lower_bound(b+1,b+1+b[0],p[i])-b; root[i]=build(); update(root[i],1,n,p[i],1); } dfs(1,n); for(i=1;i<=n;i++) { cout<<ans[i]<<endl; } return 0; }
BZOJ4636 蒟蒻的数列
-
类似 luogu P4560 [IOI2014] Wall 砖墙 ,记录区间的最大值。
-
由于只有一次询问且为全局询问,故修改时不需要下传标记,询问时再进行下传。
点击查看代码
const ll Maxm=4e4+10,Maxv=1e9+10,Maxmlogv=Maxm*(log2(Maxv)+5); ll rt_sum=0; struct SegmentTree { ll ls,rs,maxx; }tree[Maxmlogv]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) ll build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].maxx=0; return rt_sum; } void update(ll rt,ll l,ll r,ll x,ll y,ll val) { if(x<=l&&r<=y) { tree[rt].maxx=max(tree[rt].maxx,val); return; } ll mid=(l+r)/2,ans=0; if(x<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,x,y,val); } if(y>mid) { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,x,y,val); } } ll query(ll rt,ll l,ll r,ll val) { tree[rt].maxx=max(tree[rt].maxx,val); if(rt==0) { return val*(r-l+1); } if(l==r) { return tree[rt].maxx; } ll mid=(l+r)/2,ans=0; ans+=query(lson(rt),l,mid,tree[rt].maxx); ans+=query(rson(rt),mid+1,r,tree[rt].maxx); return ans; } int main() { ll m,root=build(),a,b,k,i; cin>>m; for(i=1;i<=m;i++) { cin>>a>>b>>k; b--; update(root,1,1e9,a,b,k); } cout<<query(root,1,1e9,0)<<endl; return 0; }
tgHZOJ 6013. 特殊字符串(shiki)
- 详见 初三奥赛模拟测试5 T1 特殊字符串 。
5.2
闲话
- 放假。
做题纪要
UVA1220 Party at Hali-Bula
-
多倍经验: luogu P1352 没有上司的舞会
-
设 \(f_{x,0/1}\) 表示以 \(x\) 为根的子树中,不选/选 \(x\) 时的最多人数, \(g_{x,0/1}\) 表示以 \(x\) 为根的子树中,不选/选 \(x\) 时的最多人数的方案是否唯一,状态转移方程为 \(\begin{cases} f_{x,0}=\sum\limits_{y \in Son(x)}\max(f_{y,0},f_{y,1}) \\ f_{x,1}=1+\sum\limits_{y \in Son(x)}f_{y,0} \end{cases}\) 。
点击查看代码
struct node { int nxt,to; }e[210]; int head[210],f[210][2],g[210][2],cnt=0; string s1,s2; map<string,int>a; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x) { g[x][0]=g[x][1]=f[x][1]=1; for(int i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); f[x][0]+=max(f[e[i].to][0],f[e[i].to][1]); if(f[e[i].to][0]>f[e[i].to][1]) { g[x][0]&=(g[e[i].to][0]==1); } if(f[e[i].to][0]==f[e[i].to][1]) { g[x][0]=0; } if(f[e[i].to][0]<f[e[i].to][1]) { g[x][1]&=(g[e[i].to][1]==1); } f[x][1]+=f[e[i].to][0]; g[x][1]&=(g[e[i].to][0]==1); } } int main() { int n,id,i; while(cin>>n) { if(n==0) { break; } else { cnt=id=0; a.clear(); memset(head,0,sizeof(head)); memset(e,0,sizeof(e)); memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); cin>>s1; id++; a[s1]=id; for(i=1;i<=n-1;i++) { cin>>s1>>s2; if(a.find(s1)==a.end()) { id++; a[s1]=id; } if(a.find(s2)==a.end()) { id++; a[s2]=id; } add(a[s2],a[s1]); } dfs(1); cout<<max(f[1][0],f[1][1])<<" "; if(f[1][0]>f[1][1]) { if(g[1][0]==1) { cout<<"Yes"<<endl; } else { cout<<"No"<<endl; } } if(f[1][0]==f[1][1]) { cout<<"No"<<endl; } if(f[1][0]<f[1][1]) { if(g[1][1]==1) { cout<<"Yes"<<endl; } else { cout<<"No"<<endl; } } } } return 0; }
SP30906 ADAUNIQ - Ada and Unique Vegetable
-
离散化后莫队维护即可。
点击查看代码
int a[1000010],b[1000010],cnt[1000010],L[1000010],R[1000010],pos[1000010],ans[1000010],klen,ksum; struct ask { int l,r,id,tim; }q[1000010]; struct change { int pos,col; }c[1000010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.r]==pos[b.r])?(a.tim<b.tim):(a.r<b.r)):(a.l<b.l); } void init(int n) { klen=pow(n,2.0/3); ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { sum-=(cnt[x]==1); cnt[x]++; sum+=(cnt[x]==1); } void del(int x,int &sum) { sum-=(cnt[x]==1); cnt[x]--; sum+=(cnt[x]==1); } int main() { int n,m,mm,pd,l=1,r=0,tim=0,sum=0,qcnt=0,ccnt=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } mm=n; init(n); for(i=1;i<=m;i++) { cin>>pd; if(pd==2) { qcnt++; cin>>q[qcnt].l>>q[qcnt].r; q[qcnt].l++; q[qcnt].r++; q[qcnt].id=qcnt; q[qcnt].tim=ccnt; } else { ccnt++; cin>>c[ccnt].pos>>c[ccnt].col; c[ccnt].pos++; mm++; b[mm]=c[ccnt].col; } } sort(b+1,b+1+mm); b[0]=unique(b+1,b+1+mm)-(b+1); for(i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; } for(i=n+1;i<=mm;i++) { c[i-n].col=lower_bound(b+1,b+1+b[0],c[i-n].col)-b; } sort(q+1,q+1+qcnt,q_cmp); for(i=1;i<=qcnt;i++) { while(l>q[i].l) { l--; add(a[l],sum); } while(r<q[i].r) { r++; add(a[r],sum); } while(l<q[i].l) { del(a[l],sum); l++; } while(r>q[i].r) { del(a[r],sum); r--; } while(tim<q[i].tim) { tim++; if(l<=c[tim].pos&&c[tim].pos<=r) { del(a[c[tim].pos],sum); add(c[tim].col,sum); } swap(a[c[tim].pos],c[tim].col); } while(tim>q[i].tim) { if(l<=c[tim].pos&&c[tim].pos<=r) { del(a[c[tim].pos],sum); add(c[tim].col,sum); } swap(a[c[tim].pos],c[tim].col); tim--; } ans[q[i].id]=sum; } for(i=1;i<=qcnt;i++) { cout<<ans[i]<<endl; } return 0; }
CF600E Lomsat gelral
-
自下而上线段树合并即可。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],a[200010],root[200010],ans[200010],cnt=0,rt_sum=0; struct SegmentTree { ll ls,rs,cnt,sum; }tree[2000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(ll rt) { if(tree[lson(rt)].cnt>tree[rson(rt)].cnt) { tree[rt].cnt=tree[lson(rt)].cnt; tree[rt].sum=tree[lson(rt)].sum; } if(tree[lson(rt)].cnt==tree[rson(rt)].cnt) { tree[rt].cnt=tree[lson(rt)].cnt; tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } if(tree[lson(rt)].cnt<tree[rson(rt)].cnt) { tree[rt].cnt=tree[rson(rt)].cnt; tree[rt].sum=tree[rson(rt)].sum; } } ll build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].cnt=tree[rt_sum].sum=0; return rt_sum; } void update(ll rt,ll l,ll r,ll pos,ll val) { if(l==r) { tree[rt].sum=l; tree[rt].cnt+=val; return; } ll mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val); } pushup(rt); } ll merge(ll rt1,ll rt2,ll l,ll r) { if(rt1==0) { return rt2; } if(rt2==0) { return rt1; } if(l==r) { tree[rt1].sum=l; tree[rt1].cnt+=tree[rt2].cnt; return rt1; } ll mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } ll query(ll rt,ll l,ll r,ll x,ll y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } ll mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(ll x,ll fa,ll n) { for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x,n); root[x]=merge(root[x],root[e[i].to],1,n); } } ans[x]=query(root[x],1,n,1,n); } int main() { ll n,u,v,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; root[i]=build(); update(root[i],1,n,a[i],1); } for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs(1,0,n); for(i=1;i<=n;i++) { cout<<ans[i]<<" "; } return 0; }
[ABC348E] Minimize Sum of Distances
-
多倍经验: Tree with Maximum Cost
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],siz[200010],c[200010],f[200010],g[200010],cnt=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(ll x,ll fa) { siz[x]=c[x]; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x); f[x]+=f[e[i].to]+siz[e[i].to]; siz[x]+=siz[e[i].to]; } } } void reroot(ll x,ll fa,ll n) { if(x!=1) { g[x]=g[fa]+n-siz[x]-siz[x]; } for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { reroot(e[i].to,x,n); } } } int main() { ll n,sum=0,ans=0x7f7f7f7f7f7f7f7f,u,v,i; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } for(i=1;i<=n;i++) { cin>>c[i]; sum+=c[i]; } dfs(1,0); g[1]=f[1]; reroot(1,0,sum); for(i=1;i<=n;i++) { ans=min(ans,g[i]); } cout<<ans<<endl; return 0; }
5.3
闲话
- 上午返校了。
- 小班会的时候,现班主任又 \(D\) 了我们一下。
- 详见 2024 HE中考 游记 5.3 。
- 上了 \(2\) 节化学自习, \(0.5\) 节政治和 \(1.5\) 节政治自习。感觉学校安排的没什么意义,还因此少了 \(1\) 天假期。
做题纪要
5.4
闲话
- 晚上周测是英语,学校还专门印了中考时的答题纸给我们用,平常我们只有在调考时才会用的。因作文书写不过关被现英语老师 \(D\) 了。
做题纪要
5.5
闲话
- 数学
- 最后一题第二问有歧义,数学老师卷子都判完了才被通知我们写的是对的。
- 最后一题最后一问本来答案写对了,结果我给擦了。
- 文综
- 永远看不清的漫画。
- 学校非常体谅我们,所以 \(1.5h\) 两张文综卷。
- 晚上开班会。现班主任给喂了点鸡汤,交代了些理化实验课的要求。
做题纪要
5.6
闲话
- 下午高一的开始讲线段树合并了, \(miaomiao\) 让没学的去跟着高一的一起听。
- 英语二轮复习资料怎么净是些高考复习资料,提前进入高考备考阶段是吧?
做题纪要
tgHZOJ 6014. 宝可梦(pokemon)
- 详见 初三奥赛模拟测试5 T2 宝可梦 。
tgHZOJ 6015. 矩阵(matrix)
- 详见 初三奥赛模拟测试5 T3 矩阵 。
luogu P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
-
树上点差分过程同 luogu P3128 [USACO15DEC] Max Flow P | luogu P3258 [JLOI2014] 松鼠的新家。
-
然后线段树合并维护即可。
点击查看代码
struct node { int nxt,to; }e[200010]; int head[200010],dep[200010],dis[200010],fa[200010][20],x[200010],y[200010],z[200010],b[200010],d[200010],ans[200010],root[200010],N,cnt=0,rt_sum=0; struct SegmentTree { int ls,rs,sum,cnt; }tree[7000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(int rt) { if(tree[lson(rt)].cnt>tree[rson(rt)].cnt) { tree[rt].cnt=tree[lson(rt)].cnt; tree[rt].sum=tree[lson(rt)].sum; } if(tree[lson(rt)].cnt==tree[rson(rt)].cnt) { tree[rt].cnt=tree[lson(rt)].cnt; tree[rt].sum=min(tree[lson(rt)].sum,tree[rson(rt)].sum); } if(tree[lson(rt)].cnt<tree[rson(rt)].cnt) { tree[rt].cnt=tree[rson(rt)].cnt; tree[rt].sum=tree[rson(rt)].sum; } } int build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].cnt=tree[rt_sum].sum=0; return rt_sum; } void update(ll rt,ll l,ll r,ll pos,ll val) { if(l==r) { tree[rt].cnt+=val; tree[rt].sum=(tree[rt].cnt>=1)*l; return; } int mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val); } pushup(rt); } int merge(int rt1,int rt2,int l,int r) { if(rt1==0) { return rt2; } if(rt2==0) { return rt1; } if(l==r) { tree[rt1].cnt+=tree[rt2].cnt; tree[rt1].sum=(tree[rt1].cnt>=1)*l; return rt1; } int mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } int query(int rt,int l,int r,int x,int y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } int mid=(l+r)/2,ans=0x7f7f7f7f; if(x<=mid) { ans=min(ans,query(lson(rt),l,mid,x,y)); } if(y>mid) { ans=min(ans,query(rson(rt),mid+1,r,x,y)); } return ans; } void dfss(int x,int father,int n) { for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfss(e[i].to,x,n); root[x]=merge(root[x],root[e[i].to],1,n); } } ans[x]=query(root[x],1,n,1,n); } void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int father) { fa[x][0]=father; dep[x]=dep[father]+1; for(int i=1;(1<<i)<=dep[x];i++) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs(e[i].to,x); } } } int lca(int x,int y) { if(dep[x]>dep[y]) { swap(x,y); } for(int i=N;i>=0;i--) { if(dep[x]+(1<<i)<=dep[y]) { y=fa[y][i]; } } if(x==y) { return x; } else { for(int i=N;i>=0;i--) { if(fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]; } } int main() { int n,m,u,v,ls,i; cin>>n>>m; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } N=log2(n)+1; dfs(1,0); for(i=1;i<=m;i++) { cin>>x[i]>>y[i]>>z[i]; b[i]=z[i]; } sort(b+1,b+1+m); b[0]=unique(b+1,b+1+m)-(b+1); for(i=1;i<=n;i++) { root[i]=build(); } for(i=1;i<=m;i++) { ls=lower_bound(b+1,b+1+b[0],z[i])-b; d[ls]=z[i]; z[i]=ls; ls=lca(x[i],y[i]); update(root[x[i]],1,b[0],z[i],1); update(root[y[i]],1,b[0],z[i],1); update(root[ls],1,b[0],z[i],-1); if(fa[ls][0]!=0) { update(root[fa[ls][0]],1,b[0],z[i],-1); } } dfss(1,0,b[0]); for(i=1;i<=n;i++) { cout<<d[ans[i]]<<endl; } return 0; }
luogu P3201 [HNOI2009] 梦幻布丁
-
启发式合并板子。
- 核心思想是把“小的结构”合并到“大的结构”,且只增加“ 小的结构”的查询代价。
- 启发式合并常与并查集、线段树合并、平衡树一起应用。
- 由于每次集合的大小至少翻倍,故每个元素最多合并 \(\log m\) 次。
点击查看代码
int fa[1000010],siz[1000010],a[1000010]; vector<int>s[1000010]; void dsu_union(int x,int y,int &ans,int n) { if(x!=y) { if(siz[fa[x]]>siz[fa[y]])//把小的合并到大的里 { swap(fa[x],fa[y]); } x=fa[x]; y=fa[y]; if(siz[x]!=0) { for(int i=0;i<s[x].size();i++) { if(1<=s[x][i]-1) { ans-=(a[s[x][i]-1]==y); } if(s[x][i]+1<=n) { ans-=(a[s[x][i]+1]==y); } } for(int i=0;i<s[x].size();i++) { a[s[x][i]]=y;//不能算重 s[y].push_back(s[x][i]); } siz[y]+=siz[x]; siz[x]=0; s[x].clear(); } } } int main() { int n,m,pd,x,y,ans=0,i; cin>>n>>m; for(i=1;i<=1000000;i++) { fa[i]=i; } for(i=1;i<=n;i++) { cin>>a[i]; s[fa[a[i]]].push_back(i); siz[fa[a[i]]]++; ans+=(a[i]!=a[i-1]); } for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>x>>y; dsu_union(x,y,ans,n); } else { cout<<ans<<endl; } } return 0; }
luogu P3224 [HNOI2012] 永无乡
-
并查集维护连通关系即可。
点击查看代码
int fa[100010],c[100010],root[100010],rt_sum=0; struct SegmentTree { int ls,rs,sum,id; }tree[3000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } int build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=tree[rt_sum].id=0; return rt_sum; } void update(int rt,int l,int r,int pos,int val,int id) { if(l==r) { tree[rt].id=id; tree[rt].sum+=val; return; } int mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,pos,val,id); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val,id); } pushup(rt); } int merge(int rt1,int rt2,int l,int r) { if(rt1==0) { return rt2; } if(rt2==0) { return rt1; } if(l==r) { tree[rt1].id=(tree[rt1].id!=0)?tree[rt1].id:tree[rt2].id; tree[rt1].sum+=tree[rt2].sum; return rt1; } int mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } int query(int rt,int l,int r,int x,int y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } int kth_min(int rt,int l,int r,int k) { if(l==r) { return tree[rt].id; } int mid=(l+r)/2; if(k<=tree[lson(rt)].sum) { return kth_min(lson(rt),l,mid,k); } else { return kth_min(rson(rt),mid+1,r,k-tree[lson(rt)].sum); } } int find(int x) { return (fa[x]==x)?x:fa[x]=find(fa[x]); } void dsu_merge(int x,int y,int n) { x=find(x); y=find(y); if(x!=y) { root[x]=merge(root[x],root[y],1,n); fa[y]=x; } } int main() { int n,m,q,u,v,i; char pd; cin>>n>>m; for(i=1;i<=n;i++) { cin>>c[i]; root[i]=build(); fa[i]=i; update(root[i],1,n,c[i],1,i); } for(i=1;i<=m;i++) { cin>>u>>v; dsu_merge(u,v,n); } cin>>q; for(i=1;i<=q;i++) { cin>>pd>>u>>v; if(pd=='Q') { u=find(u); cout<<((query(root[u],1,n,1,n)<v)?-1:kth_min(root[u],1,n,v))<<endl; } else { dsu_merge(u,v,n); } } return 0; }
5.7
闲话
- 第一次化学实验课,还行。
- 学校非常体谅我们,所以在信息中考前安排了考前突击,充分利用中午的时间,我们班午休的时候安排了一节信息课,和第 \(6\) 节信息课连上了。
做题纪要
BZOJ4399 魔法少女LJJ
-
\(3,4\) 操作区间修改的同时将其删除即可,同时再加回来。
-
\(6\) 操作的权值乘积大小判断考虑缩小值域,用 \(\log\) 维护即可。
- 运算规则为 \(\log (a \times b)=\log a+\log b\) 。
-
\(8,9\) 操作诈骗题。
点击查看代码
int root[400010],fa[400010],siz[400010],rt_sum=0; struct SegmentTree { int ls,rs,sum,lazy; double mul; }tree[15000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; tree[rt].mul=tree[lson(rt)].mul+tree[rson(rt)].mul; } int build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=tree[rt_sum].mul=tree[rt_sum].lazy=0; return rt_sum; } void pushdown(int rt) { if(tree[rt].lazy!=0) { if(lson(rt)!=0) { tree[lson(rt)].sum=tree[lson(rt)].mul=0; tree[lson(rt)].lazy=1; } if(rson(rt)!=0) { tree[rson(rt)].sum=tree[rson(rt)].mul=0; tree[rson(rt)].lazy=1; } tree[rt].lazy=0; } } void update1(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].sum+=val; tree[rt].mul+=log(pos)*val; return; } pushdown(rt); int mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update1(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update1(rson(rt),mid+1,r,pos,val); } pushup(rt); } int update2(int rt,int l,int r,int x,int y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { int ans=tree[rt].sum; tree[rt].sum=tree[rt].mul=0; tree[rt].lazy=1; return ans; } pushdown(rt); int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=update2(lson(rt),l,mid,x,y); } if(y>mid) { ans+=update2(rson(rt),mid+1,r,x,y); } return ans; } int merge(int rt1,int rt2,int l,int r) { if(rt1==0) { return rt2; } if(rt2==0) { return rt1; } pushdown(rt1); pushdown(rt2); if(l==r) { tree[rt1].sum+=tree[rt2].sum; tree[rt1].mul+=tree[rt2].mul; return rt1; } int mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } double query(int rt,int l,int r,int x,int y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].mul; } int mid=(l+r)/2; double ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } int kth_min(int rt,int l,int r,int k) { if(l==r) { return l; } pushdown(rt); int mid=(l+r)/2; if(k<=tree[lson(rt)].sum) { return kth_min(lson(rt),l,mid,k); } else { return kth_min(rson(rt),mid+1,r,k-tree[lson(rt)].sum); } } int dsu_find(int x) { return (fa[x]==x)?x:fa[x]=dsu_find(fa[x]); } void dsu_merge(int x,int y,int n) { x=dsu_find(x); y=dsu_find(y); if(x!=y) { root[x]=merge(root[x],root[y],1,n); fa[y]=x; siz[x]+=siz[y]; } } int main() { int m=0,n=0,pd,u,v,i; cin>>m; for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>u; n++; root[n]=build(); siz[n]++; fa[n]=n; update1(root[n],1,1000000000,u,1); } if(pd==2) { cin>>u>>v; dsu_merge(u,v,1000000000); } if(pd==3) { cin>>u>>v; u=dsu_find(u); update1(root[u],1,1000000000,v,update2(root[u],1,1000000000,1,v-1)); } if(pd==4) { cin>>u>>v; u=dsu_find(u); update1(root[u],1,1000000000,v,update2(root[u],1,1000000000,v+1,1000000000)); } if(pd==5) { cin>>u>>v; u=dsu_find(u); cout<<kth_min(root[u],1,1000000000,v)<<endl; } if(pd==6) { cin>>u>>v; u=dsu_find(u); v=dsu_find(v); cout<<(query(root[u],1,1000000000,1,1000000000)>query(root[v],1,1000000000,1,1000000000))<<endl; } if(pd==7) { cin>>u; cout<<siz[dsu_find(u)]<<endl; } } return 0; }
luogu P4577 [FJOI2018] 领导集团问题
-
多倍经验: BZOJ4919 大根堆
- 自下而上维护最长上升子序列。
- 加入 \(x\) 前,替换第一个 \(\ge w_{x}\) 的数。
-
自下而上维护最长不上升子序列,观察到各棵子树间互不影响。
-
合并时套个
multiset
启发式合并。 -
加入 \(x\) 后,类似 最长不下降子序列的 \(O(n \log n)\) 做法 中的 \(d\) 数组,替换最后一个 \(<w_{x}\) 的数。
-
注意此时
multiset
中存的数并不是最终结果,其中从右往左数第 \(i\) 个数表示以 \(x\) 为根的子树中,长度为 \(i\) 的不上升子序列末尾的最大值。点击查看代码
struct node { int nxt,to; }e[400010]; int head[400010],c[400010],cnt=0; multiset<int>s[400010]; multiset<int>::iterator it; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dsu_union(int x,int y) { if(s[x].size()>s[y].size()) { swap(s[x],s[y]); } for(it=s[x].begin();it!=s[x].end();it++) { s[y].insert(*it); } s[x].clear(); } void dfs(int x) { for(int i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); dsu_union(e[i].to,x); } s[x].insert(c[x]); it=s[x].lower_bound(c[x]); if(it!=s[x].begin()) { s[x].erase(--it); } } int main() { int n,u,v,i; cin>>n; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=2;i<=n;i++) { cin>>u; v=i; add(u,v); } dfs(1); cout<<s[1].size()<<endl; return 0; }
5.8
闲话
- 第一次物理实验课,不会拿火柴,点蜡烛的时候被火柴烧了手 \(3\) 次。
- 晚三 \(miaomiao\) 把 @minecraft418 和 @HS_xh 交出去说了下特招的事情,但今年特招的事情早在一个月前就在 HZ 公众号上发了,奇怪的是 \(miaomiao\) 并没有对其转发。
做题纪要
luogu P4219 [BJOI2014] 大融合
-
对于每组询问,以 \(x\) 为根的子树内的节点个数乘以以 \(y\) 为根的子树内的节点个数即为所求。
-
考虑将其离线下来,建完图后处理出每个点的 \(DFS\) 序。此时判定一个点是否在某一棵子树内可以通过 \(DFS\) 序判断。
-
故对 \(DFS\) 序建立权值线段树。询问时钦定 \(dfn_{u_{i}}>dfn_{v_{i}}\) ,则 \(\sum\limits_{x=1}^{n}[dfn_{u_{i}} \le dfn_{x} \le out_{u_{i}}]\) 即为以 \(u_{i}\) 为根的子树内的节点个数,类似地有 \(\sum\limits_{x=1}^{n}[dfn_{x}<dfn_{u_{i}} \lor dfn_{x}>out_{u_{i}}]\) 即为所求。
点击查看代码
struct node { int nxt,to; }e[200010]; int head[200010],root[200010],dfn[200010],out[200010],u[200010],v[200010],fa[200010],rt_sum=0,cnt=0,tot=0; char pd[200010]; struct SegmentTree { int ls,rs,sum; }tree[4000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } int build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].sum+=val; return; } int mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val); } pushup(rt); } int merge(int rt1,int rt2,int l,int r) { if(rt1==0) { return rt2; } if(rt2==0) { return rt1; } if(l==r) { tree[rt1].sum+=tree[rt2].sum; return rt1; } int mid=(l+r)/2; lson(rt1)=merge(lson(rt1),lson(rt2),l,mid); rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r); pushup(rt1); return rt1; } int query(int rt,int l,int r,int x,int y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } void dfs(int x,int fa,int root) { tot++; dfn[x]=tot; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x,root); } } out[x]=tot; } void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } int dsu_find(int x) { return (fa[x]==x)?x:fa[x]=dsu_find(fa[x]); } void dsu_merge(int x,int y,int n) { x=dsu_find(x); y=dsu_find(y); if(x!=y) { root[x]=merge(root[x],root[y],1,n); fa[y]=x; } } int main() { int n,m,rt,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>pd[i]>>u[i]>>v[i]; if(pd[i]=='A') { add(u[i],v[i]); add(v[i],u[i]); } } for(i=1;i<=n;i++) { fa[i]=i; root[i]=build(); if(dfn[i]==0) { dfs(i,i,i); } update(root[i],1,n,dfn[i],1); } for(i=1;i<=m;i++) { if(pd[i]=='A') { dsu_merge(u[i],v[i],n); } else { rt=dsu_find(u[i]); if(dfn[u[i]]<dfn[v[i]]) { swap(u[i],v[i]); } cout<<(query(root[rt],1,n,1,dfn[u[i]]-1)+query(root[rt],1,n,out[u[i]]+1,n))*query(root[rt],1,n,dfn[u[i]],out[u[i]])<<endl; } } return 0; }
luogu P3379 【模板】 最近公共祖先(LCA)
-
树剖求 \(LCA\) 板子。
- 一些概念
- 重儿子:其子节点中子树最大的节点。
- 如果有多个子节点的子树大小相同且最大,那么随便找一个作为重儿子。
- 轻儿子:剩余的所有子节点。
- 重/轻边:父亲节点和重/轻儿子连成的边。
- 重/轻链:由多条重/轻边首尾连接而成的路径。
- 特别地,把落单的节点也当做重链。
- 重儿子:其子节点中子树最大的节点。
- 性质
- 整棵树会被完全剖分成若干条重链,且树上每个节点属于且仅属于一条重链。
- 重链的起点不一定是重儿子。
- 树上的每条路径都可以被拆分成不超过 \(O(\log n)\) 条重链。
- 由于向下经过一条轻边时,所有子树的大小至少会除以 \(2\) 。对于树上的任意一条路径 \((x,y)\) ,将其拆分成 \((x,LCA(x,y))\) 和 \((LCA(x,y),y)\) 两条路径,两条路径分别最多走 \(O(\log n)\) 次。
- 剖分时重边优先遍历使得其 \(DFS\) 序是连续的。按照 \(dfn\) 排序后的序列即为剖分后的链。
- 算法流程
- 第一遍 \(DFS\) ,处理出子树大小、深度、重儿子、父亲节点。
- 第二遍 \(DFS\) ,处理出所在重链的链头(初始化为节点本身)。
- 求 \(LCA\)
- 对于节点 \(u,v\) 不断向上跳重链,当跳到同一条重链上时,深度较小的节点即为 \(LCA(u,v)\) 。
点击查看代码
struct node { int nxt,to; }e[1000010]; int head[1000010],siz[1000010],fa[1000010],dep[1000010],son[1000010],top[1000010],cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int father,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],x,id);//优先遍历 for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]])//选择深度较大的往上跳 { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]<dep[v])?u:v; } int main() { int n,m,rt,u,v,i; cin>>n>>m>>rt; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs1(rt,0); dfs2(rt,0,rt); for(i=1;i<=m;i++) { cin>>u>>v; cout<<lca(u,v)<<endl; } return 0; }
- 一些概念
5.9
闲话
- 详见 2024 HE中考 游记 5.9 。
- @wkh2008 因为 【数据删除】 被 \(miaomiao\) 停了一周的奥赛课,这下只能去上物理奥赛了。
- 因讨论声过大,貌似打扰到 \(huge\) 练书法了,被 \(D\) 了。
做题纪要
luogu P3384 【模板】 重链剖分/树链剖分
-
路径修改,子树修改,路径查询,子树查询板子。
- 在第二遍 \(DFS\) 时处理出此时的 \(DFS\) 序,将其拍到序列上使用线段树维护。
- 修改/查询路径信息时,不断向上跳重链维护。
- 修改/查询子树信息时,将其转化为连续区间维护。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],cnt=0,tot=0; struct SegmentTree { ll l,r,sum,lazy; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt,ll p) { tree[rt].sum=(tree[lson(rt)].sum+tree[rson(rt)].sum)%p; } void build(ll rt,ll l,ll r,ll p) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]%p; tree[rt].lazy=0; return; } ll mid=(l+r)/2; build(lson(rt),l,mid,p); build(rson(rt),mid+1,r,p); pushup(rt,p); } void pushdown(ll rt,ll p) { if(tree[rt].lazy!=0) { tree[lson(rt)].sum=(tree[lson(rt)].sum+tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1)%p)%p; tree[rson(rt)].sum=(tree[rson(rt)].sum+tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1)%p)%p; tree[lson(rt)].lazy=(tree[lson(rt)].lazy+tree[rt].lazy)%p; tree[rson(rt)].lazy=(tree[rson(rt)].lazy+tree[rt].lazy)%p; tree[rt].lazy=0; } } void update(ll rt,ll x,ll y,ll val,ll p) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].sum=(tree[rt].sum+val*(tree[rt].r-tree[rt].l+1))%p; tree[rt].lazy=(tree[rt].lazy+val)%p; return; } pushdown(rt,p); ll mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val,p); } if(y>mid) { update(rson(rt),x,y,val,p); } pushup(rt,p); } ll query(ll rt,ll x,ll y,ll p) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt,p); ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans=(ans+query(lson(rt),x,y,p))%p; } if(y>mid) { ans=(ans+query(rson(rt),x,y,p))%p; } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } out[x]=tot; } void update1(ll u,ll v,ll val,ll p) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { update(1,dfn[top[u]],dfn[u],val,p); u=fa[top[u]]; } else { update(1,dfn[top[v]],dfn[v],val,p); v=fa[top[v]]; } } if(dep[u]<dep[v]) { update(1,dfn[u],dfn[v],val,p); } else { update(1,dfn[v],dfn[u],val,p); } } void update2(ll u,ll val,ll p) { update(1,dfn[u],out[u],val,p); } ll query1(ll u,ll v,ll p) { ll ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans=(ans+query(1,dfn[top[u]],dfn[u],p))%p; u=fa[top[u]]; } else { ans=(ans+query(1,dfn[top[v]],dfn[v],p))%p; v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans=(ans+query(1,dfn[u],dfn[v],p))%p; } else { ans=(ans+query(1,dfn[v],dfn[u],p))%p; } return ans; } ll query2(ll u,ll p) { return query(1,dfn[u],out[u],p); } int main() { ll n,m,rt,p,pd,u,v,w,i; cin>>n>>m>>rt>>p; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs1(rt,0); dfs2(rt,0,rt); build(1,1,n,p); for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>u>>v>>w; update1(u,v,w,p); } if(pd==2) { cin>>u>>v; cout<<query1(u,v,p)<<endl; } if(pd==3) { cin>>u>>w; update2(u,w,p); } if(pd==4) { cin>>u; cout<<query2(u,p)<<endl; } } return 0; }
AT_abc294_g [ABC294G] Distance Queries on a Tree
-
多倍经验: luogu P4114 Qtree1 | SP375 QTREE - Query on a tree | luogu P1505 [国家集训队] 旅游
-
边权转点权板子。
- 将边权作为深度较大一段的节点的点权。
- 查询时会多算一个 \(LCA(u,v)\) 的贡献,要注意减去。
-
路径修改,路径查询板子。
点击查看代码
struct node { ll nxt,to; }e[400010]; ll head[400010],c[400010],cc[400010],siz[400010],fa[400010],dep[400010],son[400010],top[400010],dfn[400010],u[400010],v[400010],w[400010],cnt=0,tot=0; struct SegmentTree { ll l,r,sum; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(ll rt,ll pos,ll val) { if(tree[rt].l==tree[rt].r) { tree[rt].sum=val; return; } ll mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) { update(lson(rt),pos,val); } else { update(rson(rt),pos,val); } pushup(rt); } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } void update1(ll pos,ll val) { if(dep[u[pos]]>dep[v[pos]]) { update(1,dfn[u[pos]],val); } else { update(1,dfn[v[pos]],val); } } ll query1(ll u,ll v) { ll ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans+=query(1,dfn[top[u]],dfn[u]); u=fa[top[u]]; } else { ans+=query(1,dfn[top[v]],dfn[v]); v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans+=query(1,dfn[u]+1,dfn[v]); } else { ans+=query(1,dfn[v]+1,dfn[u]); } return ans; } int main() { ll n,m,x,y,pd,i; cin>>n; for(i=1;i<=n-1;i++) { cin>>u[i]>>v[i]>>w[i]; add(u[i],v[i]); add(v[i],u[i]); } dfs1(1,0); for(i=1;i<=n-1;i++) { if(dep[u[i]]>dep[v[i]]) { c[u[i]]=w[i]; } else { c[v[i]]=w[i]; } } dfs2(1,0,1); build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>pd>>x>>y; if(pd==1) { update1(x,y); } else { cout<<query1(x,y)<<endl; } } return 0; }
luogu P2590 [ZJOI2008] 树的统计
-
单点修改,路径查询板子。
点击查看代码
struct node { int nxt,to; }e[60010]; int head[60010],c[60010],cc[60010],siz[60010],fa[60010],dep[60010],son[60010],top[60010],dfn[60010],cnt=0,tot=0; struct SegmentTree { int l,r,sum,maxx; }tree[120010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); } void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=tree[rt].maxx=cc[l]; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(int rt,int pos,int val) { if(tree[rt].l==tree[rt].r) { tree[rt].sum=tree[rt].maxx=val; return; } int mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) { update(lson(rt),pos,val); } else { update(rson(rt),pos,val); } pushup(rt); } pair<int,int> query(int rt,int x,int y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return make_pair(tree[rt].sum,tree[rt].maxx); } int mid=(tree[rt].l+tree[rt].r)/2; pair<int,int>ans=make_pair(0,-0x7f7f7f7f); if(x<=mid) { pair<int,int>num=query(lson(rt),x,y); ans.first+=num.first; ans.second=max(ans.second,num.second); } if(y>mid) { pair<int,int>num=query(rson(rt),x,y); ans.first+=num.first; ans.second=max(ans.second,num.second); } return ans; } void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int father,int id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } void update1(int pos,int val) { update(1,dfn[pos],val); } int query1(int u,int v) { int ans=-0x7f7f7f7f; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans=max(ans,query(1,dfn[top[u]],dfn[u]).second); u=fa[top[u]]; } else { ans=max(ans,query(1,dfn[top[v]],dfn[v]).second); v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans=max(ans,query(1,dfn[u],dfn[v]).second); } else { ans=max(ans,query(1,dfn[v],dfn[u]).second); } return ans; } int query2(int u,int v) { int ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans+=query(1,dfn[top[u]],dfn[u]).first; u=fa[top[u]]; } else { ans+=query(1,dfn[top[v]],dfn[v]).first; v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans+=query(1,dfn[u],dfn[v]).first; } else { ans+=query(1,dfn[v],dfn[u]).first; } return ans; } int main() { int n,m,u,v,i; string pd; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } for(i=1;i<=n;i++) { cin>>c[i]; } dfs1(1,0); dfs2(1,0,1); build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>pd>>u>>v; if(pd=="CHANGE") { update1(u,v); } if(pd=="QMAX") { cout<<query1(u,v)<<endl; } if(pd=="QSUM") { cout<<query2(u,v)<<endl; } } return 0; }
luogu P3178 [HAOI2015] 树上操作
-
单点修改,子树修改,路径查询板子。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],cnt=0,tot=0; struct SegmentTree { ll l,r,sum,lazy; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]; tree[rt].lazy=0; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(ll rt) { if(tree[rt].lazy!=0) { tree[lson(rt)].sum+=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1); tree[rson(rt)].sum+=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1); tree[lson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].lazy+=tree[rt].lazy; tree[rt].lazy=0; } } void update(ll rt,ll x,ll y,ll val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1); tree[rt].lazy+=val; return; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val); } if(y>mid) { update(rson(rt),x,y,val); } pushup(rt); } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } out[x]=tot; } void update1(ll pos,ll val) { update(1,dfn[pos],dfn[pos],val); } void update2(ll pos,ll val) { update(1,dfn[pos],out[pos],val); } ll query1(ll u,ll v) { ll ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans+=query(1,dfn[top[u]],dfn[u]); u=fa[top[u]]; } else { ans+=query(1,dfn[top[v]],dfn[v]); v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans+=query(1,dfn[u],dfn[v]); } else { ans+=query(1,dfn[v],dfn[u]); } return ans; } int main() { ll n,m,pd,u,v,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs1(1,0); dfs2(1,0,1); build(1,1,n); for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>u>>v; update1(u,v); } if(pd==2) { cin>>u>>v; update2(u,v); } if(pd==3) { cin>>u; v=1; cout<<query1(u,v)<<endl; } } return 0; }
luogu P1967 [NOIP2013 提高组] 货车运输
-
套个 Kruskal 重构树即可,详见 UVA12655 Trucks 题解 。
点击查看代码
struct node { int nxt,from,to,w; }e[100010],E[100010]; int head[100010],f[100010],vis[100010],c[100010],cc[100010],siz[100010],fa[100010],dep[100010],son[100010],top[100010],dfn[100010],u[100010],v[100010],w[100010],cnt=0,tot=0,mm=0; struct SegmentTree { int l,r,minn; }tree[400010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn); } void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].minn=cc[l]; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } int query(int rt,int x,int y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].minn; } int mid=(tree[rt].l+tree[rt].r)/2,ans=0x7f7f7f7f; if(x<=mid) { ans=min(ans,query(lson(rt),x,y)); } if(y>mid) { ans=min(ans,query(rson(rt),x,y)); } return ans; } void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int father,int id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } int query1(int u,int v) { int ans=0x7f7f7f7f; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans=min(ans,query(1,dfn[top[u]],dfn[u])); u=fa[top[u]]; } else { ans=min(ans,query(1,dfn[top[v]],dfn[v])); v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans=min(ans,query(1,dfn[u]+1,dfn[v])); } else { ans=min(ans,query(1,dfn[v]+1,dfn[u])); } return ans; } bool cmp(node a,node b) { return a.w>b.w; } int dsu_find(int x) { return (f[x]==x)?x:f[x]=dsu_find(f[x]); } void dsu_merge(int x,int y,int z) { int xx=dsu_find(x); int yy=dsu_find(y); if(xx!=yy) { f[yy]=xx; mm++; u[mm]=x; v[mm]=y; w[mm]=z; add(u[mm],v[mm]); add(v[mm],u[mm]); } } void kruskal(int m) { sort(E+1,E+1+m,cmp); for(int i=1;i<=m;i++) { dsu_merge(E[i].from,E[i].to,E[i].w); } } int main() { int n,m,q,x,y,i; cin>>n>>m; for(i=1;i<=n;i++) { f[i]=i; } for(i=1;i<=m;i++) { cin>>E[i].from>>E[i].to>>E[i].w; } kruskal(m); for(i=1;i<=n;i++) { if(dep[i]==0) { vis[i]=1; dfs1(i,0); } } for(i=1;i<=mm;i++) { if(dep[u[i]]>dep[v[i]]) { c[u[i]]=w[i]; } else { c[v[i]]=w[i]; } } for(i=1;i<=n;i++) { if(vis[i]==1) { dfs2(i,0,i); } } build(1,1,n); cin>>q; for(i=1;i<=q;i++) { cin>>x>>y; cout<<((dsu_find(x)==dsu_find(y))?query1(x,y):-1)<<endl; } return 0; }
luogu P3833 [SHOI2012] 魔法树
-
路径修改,子树查询板子。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],din[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],cnt=0,tot=0; struct SegmentTree { ll l,r,sum,lazy; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]; tree[rt].lazy=0; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(ll rt) { if(tree[rt].lazy!=0) { tree[lson(rt)].sum+=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1); tree[rson(rt)].sum+=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1); tree[lson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].lazy+=tree[rt].lazy; tree[rt].lazy=0; } } void update(ll rt,ll x,ll y,ll val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1); tree[rt].lazy+=val; return; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val); } if(y>mid) { update(rson(rt),x,y,val); } pushup(rt); } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } out[x]=tot; } void update1(ll u,ll v,ll val) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { update(1,dfn[top[u]],dfn[u],val); u=fa[top[u]]; } else { update(1,dfn[top[v]],dfn[v],val); v=fa[top[v]]; } } if(dep[u]<dep[v]) { update(1,dfn[u],dfn[v],val); } else { update(1,dfn[v],dfn[u],val); } } ll query1(ll u) { return query(1,dfn[u],out[u]); } int main() { ll n,m,u,v,w,rt=0,i; char pd; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v; u++; v++; add(u,v); din[v]++; } for(i=1;i<=n;i++) { if(din[i]==0) { rt=i; break; } } dfs1(rt,0); dfs2(rt,0,rt); build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>pd; if(pd=='A') { cin>>u>>v>>w; u++; v++; update1(u,v,w); } if(pd=='Q') { cin>>u; u++; cout<<query1(u)<<endl; } } return 0; }
luogu P3038 [USACO11DEC] Grass Planting G
-
边权转点权板子。
-
路径修改,路径查询板子。
- 修改时注意不要多算 \(LCA(u,v)\) 。
点击查看代码
struct node { int nxt,to; }e[200010]; int head[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],u[200010],v[200010],w[200010],cnt=0,tot=0; struct SegmentTree { int l,r,sum,lazy; }tree[800010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]; tree[rt].lazy=0; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(int rt) { if(tree[rt].lazy!=0) { tree[lson(rt)].sum+=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1); tree[rson(rt)].sum+=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1); tree[lson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].lazy+=tree[rt].lazy; tree[rt].lazy=0; } } void update(int rt,int x,int y,int val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1); tree[rt].lazy+=val; return; } pushdown(rt); int mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val); } if(y>mid) { update(rson(rt),x,y,val); } pushup(rt); } int query(int rt,int x,int y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt); int mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int father,int id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } void update1(int u,int v,int val) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { update(1,dfn[top[u]],dfn[u],val); u=fa[top[u]]; } else { update(1,dfn[top[v]],dfn[v],val); v=fa[top[v]]; } } if(dep[u]<dep[v]) { update(1,dfn[u]+1,dfn[v],val); } else { update(1,dfn[v]+1,dfn[u],val); } } int query1(int u,int v) { int ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans+=query(1,dfn[top[u]],dfn[u]); u=fa[top[u]]; } else { ans+=query(1,dfn[top[v]],dfn[v]); v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans+=query(1,dfn[u]+1,dfn[v]); } else { ans+=query(1,dfn[v]+1,dfn[u]); } return ans; } int main() { int n,m,x,y,i; char pd; cin>>n>>m; for(i=1;i<=n-1;i++) { cin>>u[i]>>v[i]; w[i]=0; add(u[i],v[i]); add(v[i],u[i]); } dfs1(1,0); for(i=1;i<=n-1;i++) { if(dep[u[i]]>dep[v[i]]) { c[u[i]]=w[i]; } else { c[v[i]]=w[i]; } } dfs2(1,0,1); build(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>x>>y; if(pd=='P') { update1(x,y,1); } if(pd=='Q') { cout<<query1(x,y)<<endl; } } return 0; }
SP14943 DRTREE - Dynamically-Rooted Tree
-
换根,子树查询板子。
- 子树查询
- 类似 换根 DP 的思路,我们发现换根后仅有祖先、子树、深度等会随祖先的变化而变化。
- 设 \(rt_{i}\) 表示第 \(i\) 次操作的树根, \(x_{i}\) 表示第 \(i\) 次操作的节点。
- 当 \(rt_{i}=x_{i}\) 时,等价于查询整棵树。
- 当 \(rt_{i} \ne x_{i}\) 且 \(rt_{i}\) 不在以 \(1\) 为根时以 \(x_{i}\) 为根的子树内,换根对其没有影响,等价于查询以 \(1\) 为根时以 \(x\) 为根的子树。
- 当 \(rt_{i} \ne x_{i}\) 且 \(rt_{i}\) 在以 \(1\) 为根时以 \(x_{i}\) 为根的子树内,换根对其产生影响,等价于查询整棵树除了以 \(1\) 为根时以 \(x\) 为根的子树中 \(rt_{i}\) 所在的方向的子树。可以分别求出后相减,也可以拆成两段区间求和。
- 现在问题成了如何求一个 \(y\) 使得 \(\begin{cases} y \in Son(x) \\ rt_{i} \in Subtree(y) \end{cases}\) 。
- 对于节点 \(x,rt_{i}\) 不断向上跳重链,若不在同一条重链上时就已经满足条件则直接返回;否则,当跳到同一条重链上时,深度较小的节点的重儿子即为 \(y\) 。
- 现在问题成了如何求一个 \(y\) 使得 \(\begin{cases} y \in Son(x) \\ rt_{i} \in Subtree(y) \end{cases}\) 。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],cnt=0,tot=0; struct SegmentTree { ll l,r,sum; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } out[x]=tot; } ll dirson(ll u,ll v) { if(dep[u]>dep[v]) { swap(u,v); } while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { if(fa[top[u]]==v) { return top[u]; } u=fa[top[u]]; } else { if(fa[top[v]]==u) { return top[v]; } v=fa[top[v]]; } } return (dep[u]<dep[v])?son[u]:son[v]; } ll query1(ll pos) { return query(1,dfn[pos],out[pos]); } ll query_reroot1(ll x,ll rt) { if(x==rt) { return query1(1); } else { if(dfn[x]<=dfn[rt]&&dfn[rt]<=out[x]) { return query1(1)-query1(dirson(x,rt)); } else { return query1(x); } } } int main() { ll n,m,u,v,rt=1,i; char pd; cin>>n; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=2;i<=n;i++) { cin>>u; v=i; add(u,v); add(v,u); } dfs1(1,0); dfs2(1,0,1); build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>pd; if(pd=='S') { cin>>u; cout<<query_reroot1(u,rt)<<endl; } else { cin>>rt; } } return 0; }
- 子树查询
5.10
闲话
- ftp 突然上不去了,估计是中考需要(?)。
- 详见 2024 HE中考 游记 5.10 。
- \(miaomiao\) 貌似带队去夏令营了,机房只有 \(huge,feifei,field\) 。
- @wkh2008 从教室自己回来了,机房闹腾了一会儿,被 \(huge\) \(D\) 了,“别整些没用的”。
- 下午从窗户外面看见马场那里飘来了一缕缕浓烟,然后还来了消防车,明显能闻到一股烟味, \(huge\) 只是向外看了一眼,没说什么。
- 临吃晚饭的时候,突然风变得很大,然后就下雨了,找 \(feifei\) 去要伞, \(feifei\) 说“上次把伞还回来的同学这次可以再借一把”。 @wkh2008 顺便去三机房找书,但没被 \(feifei\) 认出来还被 \(D\) 了。
- 晚上小操场那面跑操的口令突然响了多半节课,烟味还特别浓。
做题纪要
luogu P3979 遥远的国度
-
换根,路径修改,子树查询板子。
-
注意预处理的时候不要用输入的 \(rt\) 来作为根节点, BZOJ3083 遥远的国度 上有卡这个的数据点。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],cnt=0,tot=0; struct SegmentTree { ll l,r,minn,lazy; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn); } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].minn=cc[l]; tree[rt].lazy=0; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(ll rt) { if(tree[rt].lazy!=0) { tree[lson(rt)].minn=tree[rt].lazy; tree[rson(rt)].minn=tree[rt].lazy; tree[lson(rt)].lazy=tree[rt].lazy; tree[rson(rt)].lazy=tree[rt].lazy; tree[rt].lazy=0; } } void update(ll rt,ll x,ll y,ll val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].minn=val; tree[rt].lazy=val; return; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val); } if(y>mid) { update(rson(rt),x,y,val); } pushup(rt); } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].minn; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2,ans=0x7f7f7f7f7f7f7f7f; if(x<=mid) { ans=min(ans,query(lson(rt),x,y)); } if(y>mid) { ans=min(ans,query(rson(rt),x,y)); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } out[x]=tot; } void update1(ll u,ll v,ll val) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { update(1,dfn[top[u]],dfn[u],val); u=fa[top[u]]; } else { update(1,dfn[top[v]],dfn[v],val); v=fa[top[v]]; } } if(dep[u]<dep[v]) { update(1,dfn[u],dfn[v],val); } else { update(1,dfn[v],dfn[u],val); } } ll dirson(ll u,ll v) { if(dep[u]>dep[v]) { swap(u,v); } while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { if(fa[top[u]]==v) { return top[u]; } u=fa[top[u]]; } else { if(fa[top[v]]==u) { return top[v]; } v=fa[top[v]]; } } return (dep[u]<dep[v])?son[u]:son[v]; } ll query1(ll pos) { return query(1,dfn[pos],out[pos]); } ll query_reroot1(ll x,ll rt,ll n) { if(x==rt) { return query1(1); } else { if(dfn[x]<=dfn[rt]&&dfn[rt]<=out[x]) { ll son=dirson(x,rt),ans=0x7f7f7f7f7f7f7f7f; if(dfn[son]-1>=1) { ans=min(ans,query(1,1,dfn[son]-1)); } if(out[son]+1<=n) { ans=min(ans,query(1,out[son]+1,n)); } return ans; } else { return query1(x); } } } int main() { ll n,m,pd,u,v,w,rt,i; cin>>n>>m; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } for(i=1;i<=n;i++) { cin>>c[i]; } cin>>rt; dfs1(1,0); dfs2(1,0,1); build(1,1,n); for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>rt; } if(pd==2) { cin>>u>>v>>w; update1(u,v,w); } if(pd==3) { cin>>u; cout<<query_reroot1(u,rt,n)<<endl; } } return 0; }
CF916E Jamie and Tree
-
换根,子树修改,求 \(LCA\) ,子树查询板子。
- 求 \(LCA\)
- 设 \(rt_{i}\) 表示第 \(i\) 次操作的树根, \(x_{i},y_{i}\) 表示第 \(i\) 次操作的节点。
- \(LCA_{rt_{i}}(x_{i},y_{i})\) 等于 \(LCA_{1}(x_{i},y_{i}),LCA_{1}(x_{i},rt_{i}),LCA_{1}(y_{i},rt_{i})\) 中深度最大的点。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],c[200010],cc[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],cnt=0,tot=0; struct SegmentTree { ll l,r,sum,lazy; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=cc[l]; tree[rt].lazy=0; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(ll rt) { if(tree[rt].lazy!=0) { tree[lson(rt)].sum+=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1); tree[rson(rt)].sum+=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1); tree[lson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].lazy+=tree[rt].lazy; tree[rt].lazy=0; } } void update(ll rt,ll x,ll y,ll val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1); tree[rt].lazy+=val; return; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val); } if(y>mid) { update(rson(rt),x,y,val); } pushup(rt); } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll father,ll id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } out[x]=tot; } ll lca(ll u,ll v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]<dep[v])?u:v; } ll lca_reroot(ll u,ll v,ll rt) { ll lca1=lca(u,v),lca2=lca(u,rt),lca3=lca(v,rt),ans; ans=(dep[lca1]>=dep[lca2])?lca1:lca2; ans=(dep[ans]>=dep[lca3])?ans:lca3; return ans; } ll dirson(ll u,ll v) { if(dep[u]>dep[v]) { swap(u,v); } while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { if(fa[top[u]]==v) { return top[u]; } u=fa[top[u]]; } else { if(fa[top[v]]==u) { return top[v]; } v=fa[top[v]]; } } return (dep[u]<dep[v])?son[u]:son[v]; } void update1(ll pos,ll val) { update(1,dfn[pos],out[pos],val); } void update_reroot1(ll x,ll rt,ll val) { if(x==rt) { update1(1,val); } else { if(dfn[x]<=dfn[rt]&&dfn[rt]<=out[x]) { update1(1,val); update1(dirson(x,rt),-val); } else { update1(x,val); } } } void update2(ll u,ll v,ll val,ll rt) { update_reroot1(lca_reroot(u,v,rt),rt,val); } ll query1(ll pos) { return query(1,dfn[pos],out[pos]); } ll query_reroot1(ll x,ll rt) { if(x==rt) { return query1(1); } else { if(dfn[x]<=dfn[rt]&&dfn[rt]<=out[x]) { return query1(1)-query1(dirson(x,rt)); } else { return query1(x); } } } int main() { ll n,m,pd,u,v,w,rt=1,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>c[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs1(1,0); dfs2(1,0,1); build(1,1,n); for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>rt; } if(pd==2) { cin>>u>>v>>w; update2(u,v,w,rt); } if(pd==3) { cin>>u; cout<<query_reroot1(u,rt)<<endl; } } return 0; }
- 求 \(LCA\)
luogu P2680 [NOIP2015 提高组] 运输计划
-
最大值最小,考虑二分答案。
-
找到所有路径长度大于所二分答案的的边,记录其数量。从贪心的角度分析,从边被经过的次数等于前面所记录的数量的边中选择最长的改造成虫洞即可。
-
求每条边被经过的次数,需要树上边差分,过程同 CF191C Fools and Roads 。
点击查看代码
struct node { int nxt,to,w,id; }e[600010]; int head[600010],d[600010],ans[600010],x[600010],y[600010],lcaxy[600010],len[600010],siz[600010],fa[600010],dep[600010],son[600010],top[600010],dis[600010],w[600010],cnt=0; void add(int u,int v,int w,int id) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; e[cnt].id=id; head[u]=cnt; } void dfs1(int x,int father,int w) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; dis[x]=dis[father]+w; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x,e[i].w); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int father,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],x,id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]<dep[v])?u:v; } void dfs(int x,int father) { for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs(e[i].to,x); d[x]+=d[e[i].to]; ans[e[i].id]=d[e[i].to]; } } } bool check(int mid,int n,int m,int maxx) { int sum=0,maxlen=0,i; memset(d,0,sizeof(d)); for(i=1;i<=m;i++) { if(len[i]>mid) { d[x[i]]++; d[y[i]]++; d[lcaxy[i]]-=2; sum++; } } dfs(1,0); for(i=1;i<=n-1;i++) { maxlen=max(maxlen,(ans[i]==sum)*w[i]); } return maxx-maxlen>mid; } int main() { int n,m,u,v,l=0,r=0,mid,ans=0,maxx=0,i; cin>>n>>m; for(i=1;i<=n-1;i++) { cin>>u>>v>>w[i]; r+=w[i]; add(u,v,w[i],i); add(v,u,w[i],i); } dfs1(1,0,0); dfs2(1,0,1); for(i=1;i<=n;i++) { cin>>x[i]>>y[i]; lcaxy[i]=lca(x[i],y[i]); len[i]=dis[x[i]]+dis[y[i]]-2*dis[lcaxy[i]]; maxx=max(maxx,len[i]); } while(l<=r) { mid=(l+r)/2; if(check(mid,n,m,maxx)==true) { l=mid+1; } else { ans=mid; r=mid-1; } } cout<<ans<<endl; return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18169047,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。