[NOI2021] 轻重边
1.[TK] 矩阵取数游戏<简单版> hzoi-tg-906-22.[TK] 一心净士 hzoj-tg-937-23.[TK] 盖房子 hzoi-tg#2624.[TK] 三角蛋糕 hzoi-tg#2615.[TK] 选课 hzoj-tg#2796.[TK] 三色二叉树 hzoi-tg#282 存图方法7.[TK] HH的项链 离线树状数组解法8.[TK] 理想的正方形9.[TK] Blocks 单调栈10.[TK] 送礼物11.[TK] Terrible Prime12.[TK] BLO13.[TK] Rudolf and Subway ( CodeForces #933 div.3 - G )14.[TK] Bulls And Cows S15.[TK] 颜色16.[Tkey] 与非17.[TK] CF1526B I Hate 111118.[TK] 寻宝游戏19.[CL-22] 异或和之和20.联训题单 / 集训杂题纪要21.[At_dp_w] Intervals & [At_dp_x] Tower22.CF2023D - Many Games
23.[NOI2021] 轻重边
24.[CSP-S 2024] 染色气死我了这题,还是写一下题解
首先有一个非常好的转化,你可以把给定操作转为树上颜色问题
假设将操作
因此就可以定义重边为 “端点颜色相同的边”,轻边为 “端点颜色不同的边”,这样就可以将原问题转为树上数颜色问题
转化到这里就很好做了,由于数颜色是合并连通块,可以直接树剖之后放到线段树上去做,维护当前连续段长度,左端点,右端点,然后每次尝试在中间合并(合并最多只会增加
操作要求我们支持区间修改与区间查询
对于区间修改,修改后整体覆盖的区间,其连通块个数应为
对于区间查询,同样维护长度,左端点与右端点,询问时分治并尝试在中间合并
然后是这个树剖的查询操作,这里不能无脑跳,因为我们需要合并连续段,因此必须知道当前链哪端是左边哪端是右边,这个可以通过维护一个变量来记录当前跳的是左边点还是右边点,然后通过深度低的点更靠左来判断左右端点
这个题需要特判颜色初值
因为懒得特判所以为点赋了不同初值,没想到最后发现和树剖的初值在值域上有交
#include<bits/stdc++.h>
using namespace std;
#define clearzero(x) memset((x),0,sizeof(x))
struct tree{
int lc,rc;
int lazy;
int tot;
}t[100001*4];
int wnew[100001];
#define tol (id*2)
#define tor (id*2+1)
#define mid(l,r) mid=((l)+(r))/2
void pushdown(int id,int l,int r){
if(t[id].lazy){
int mid(l,r);
t[tol]={t[id].lazy,t[id].lazy,t[id].lazy,mid-l};
t[tor]={t[id].lazy,t[id].lazy,t[id].lazy,r-(mid+1)};
t[id].lazy=0;
}
}
void update(int id){
t[id].lc=t[tol].lc;
t[id].rc=t[tor].rc;
t[id].tot=t[tol].tot+t[tor].tot+(t[tol].rc==t[tor].lc);
}
void build(int id,int l,int r){
if(l==r){
t[id]={-wnew[l],-wnew[l],0,0};
return;
}
int mid(l,r);
build(tol,l,mid);
build(tor,mid+1,r);
update(id);
}
void change(int id,int l,int r,int L,int R,int val){
if(L<=l and r<=R){
t[id]={val,val,val,r-l};
return;
}
pushdown(id,l,r);
int mid(l,r);
if(R<=mid) change(tol,l,mid,L,R,val);
else if(L>=mid+1) change(tor,mid+1,r,L,R,val);
else{
change(tol,l,mid,L,mid,val);
change(tor,mid+1,r,mid+1,R,val);
}
update(id);
}
tree ask(int id,int l,int r,int L,int R){
if(L<=l and r<=R){
return t[id];
}
pushdown(id,l,r);
int mid(l,r);
if(R<=mid) return ask(tol,l,mid,L,R);
else if(L>=mid+1) return ask(tor,mid+1,r,L,R);
tree a=ask(tol,l,mid,L,mid),b=ask(tor,mid+1,r,mid+1,R);
return {a.lc,b.rc,0,a.tot+b.tot+(a.rc==b.lc)};
}
int n,m;
vector<int>e[100001];
int deep[100001],fa[100001];
int size[100001],maxson[100001];
int id[100001],top[100001],idcnt;
int dfn[100001],dfncnt;
void dfs1(int now,int last){
fa[now]=last;
deep[now]=deep[last]+1;
dfn[last]=++dfncnt;
size[now]=1;
int maxsonsize=0;
for(int i:e[now]){
if(i!=last){
dfs1(i,now);
size[now]+=size[i];
if(size[i]>maxsonsize){
maxsonsize=size[i];
maxson[now]=i;
}
}
}
}
void dfs2(int now,int nowtop){
top[now]=nowtop;
id[now]=++idcnt;
if(!maxson[now]) return;
dfs2(maxson[now],nowtop);
for(int i:e[now]){
if(i!=fa[now] and i!=maxson[now]){
dfs2(i,i);
}
}
}
void change_path(int x,int y,int val){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
change(1,1,n,id[top[x]],id[x],val);
x=fa[top[x]];
}
if(deep[x]<deep[y]) swap(x,y);
change(1,1,n,id[y],id[x],val);
}
int ask_path(int x,int y){
tree ans1={-1-2*n,-2-2*n,0,0},ans2={-3-2*n,-4-2*n,0,0};
bool isans1=true;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]){
isans1^=1;
swap(x,y);
}
tree res=ask(1,1,n,id[top[x]],id[x]);
if(isans1){
ans1={ans1.lc,res.lc,0,ans1.tot+res.tot+(ans1.rc==res.rc)};
}
else{
ans2={res.lc,ans2.rc,0,ans2.tot+res.tot+(ans2.lc==res.rc)};
}
x=fa[top[x]];
}
if(deep[x]<deep[y]){
isans1^=1;
swap(x,y);
}
tree res=ask(1,1,n,id[y],id[x]);
if(isans1){
ans1={ans1.lc,res.lc,0,ans1.tot+res.tot+(ans1.rc==res.rc)};
}
else{
ans2={res.lc,ans2.rc,0,ans2.tot+res.tot+(ans2.lc==res.rc)};
}
return ans1.tot+ans2.tot+(ans1.rc==ans2.lc);
}
void clear(){
idcnt=0;
dfncnt=0;
clearzero(t);
clearzero(wnew);
clearzero(id);
clearzero(fa);
clearzero(deep);
clearzero(size);
clearzero(maxson);
clearzero(top);
clearzero(dfn);
for(int i=1;i<=n;++i){
e[i].clear();
}
}
int main(){
int t;cin>>t;
while(t--){
cin>>n>>m;
clear();
for(int i=1;i<=n-1;++i){
int x,y;
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
iota(wnew+1,wnew+n+1,1);
build(1,1,n);
for(int i=1;i<=m;++i){
int opt,x,y;
cin>>opt>>x>>y;
if(opt==1){
change_path(x,y,i);
}
else{
cout<<ask_path(x,y)<<'\n';
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!