本文主要是介绍HYSBZ - 2243染色——树链剖分+线段树建树技巧,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
【题目描述】
HYSBZ - 2243染色
【题目分析】
我一直没有看清楚题,以为求的是路径上出现颜色的种类,然后就写了一个区间染色的线段树进行维护,过样例的时候才发现题读错了,人家要求的是路径上出现的颜色段,所以颜色的种类不重要,重要的是每一段每一段。理所当然,我们应该用线段树维护所在区间有多少段。但是左右区间上传的时候如果边界颜色相同(左节点的右边界和右节点的左边界),那么区间个数应该减一。为此,我们还必须维护每个区间左边界和右边界分别是什么颜色以方便查询和上传。因为题目是区间修改,所以我们还要用lazy标记。(用lazy标记的时候要时时记得标记下传,就是因为单点查询的时候忘记要标记下传wa了一下午)
因为在树上,所以我们不仅仅需要判断线段树区间合并的时候左右端点颜色是否相同,还要判断每条链顶和链顶的父节点的颜色是否相同,如果相同答案减一。(每条链都在向链顶合并)
【AC代码】
#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<climits>using namespace std;const int MAXN=100005; //时刻注意数据范围
vector<int>g[MAXN];
int fa[MAXN],A[MAXN],val[MAXN],color[MAXN],pos[MAXN];
bool check[MAXN];
int siz[MAXN],son[MAXN],h[MAXN],top[MAXN];
int cnt=0,n,m;
int num[MAXN<<2],lazy[MAXN<<2];
int lc[MAXN<<2],rc[MAXN<<2];void dfs1(int u,int f)
{int i,v;siz[u]=1;son[u]=0;fa[u]=f;h[u]=h[f]+1;for(i=0;i<g[u].size();i++){v=g[u][i];if(v!=f){dfs1(v,u);siz[u]+=siz[v];if(siz[son[u]]<siz[v]) son[u]=v;}}
}
void dfs2(int u,int f,int k)
{int i,v;top[u]=k;pos[u]=++cnt;color[cnt]=val[u];if(son[u]) dfs2(son[u],u,k);for(i=0;i<g[u].size();i++){v=g[u][i];if(v!=f&&v!=son[u]) dfs2(v,u,v);}
}void pushup(int k)
{num[k]=num[k<<1]+num[k<<1|1];if(rc[k<<1]==lc[k<<1|1]) num[k]--; //如果左右区间的连接处颜色相同则答案减一lc[k]=lc[k<<1]; rc[k]=rc[k<<1|1];
}void build(int k,int l,int r)
{if(l==r){num[k]=1;lc[k]=rc[k]=color[l];return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}void pushdown(int k)
{if(lazy[k]){lazy[k<<1]=lazy[k<<1|1]=lazy[k];lc[k<<1]=lc[k<<1|1]=lazy[k];rc[k<<1]=rc[k<<1|1]=lazy[k];num[k<<1]=num[k<<1|1]=1;lazy[k]=0;}
}void ColorChange(int k,int l,int r,int L,int R,int v)
{if(l>=L && r<=R){num[k]=1; lazy[k]=v;lc[k]=v; rc[k]=v;return;}int mid=(l+r)>>1;pushdown(k);if(L<=mid) ColorChange(k<<1,l,mid,L,R,v);if(R>mid) ColorChange(k<<1|1,mid+1,r,L,R,v);pushup(k);
}int QueryInterval(int k,int l,int r,int L,int R)
{if(L<=l && r<=R){return num[k];}int mid=(l+r)/2;pushdown(k);int ret=0;if(R<=mid) return QueryInterval(k<<1,l,mid,L,R);else if(L>mid) return QueryInterval(k<<1|1,mid+1,r,L,R); //这里必须这样写,因为要考虑是否存在区间合并ret+=QueryInterval(k<<1,l,mid,L,R);ret+=QueryInterval(k<<1|1,mid+1,r,L,R);if(rc[k<<1]==lc[k<<1|1]) ret--;return ret;
}int QueryPointColor(int k,int l,int r,int x)
{if(l==r && l==x){return lc[k];}pushdown(k); //因为这里忘记了.wocint mid=(l+r)>>1;if(x<=mid) return QueryPointColor(k<<1,l,mid,x);else return QueryPointColor(k<<1|1,mid+1,r,x);
}int Findnum(int u,int v)
{memset(check,0,sizeof(check));int ans=0;while(top[u]!=top[v]){if(h[top[u]]<h[top[v]]) swap(u,v);ans+=QueryInterval(1,1,n,pos[top[u]],pos[u]);//printf("ans=%d\n",ans);//printf("QueryPointColor(1,1,n,pos[%d])=%d\nQueryPointColor(1,1,n,pos[%d])=%d\n",top[u],QueryPointColor(1,1,n,pos[top[u]]),fa[top[u]],QueryPointColor(1,1,n,pos[fa[top[u]]]));if(QueryPointColor(1,1,n,pos[top[u]]) == QueryPointColor(1,1,n,pos[fa[top[u]]])) //考虑链与链的合并ans--;u=fa[top[u]];}if(h[u]<h[v]) swap(u,v);ans+=QueryInterval(1,1,n,pos[v],pos[u]);//printf("ans=%d\n",ans);return ans;
}void update(int u,int v,int w)
{while(top[u]!=top[v]){if(h[top[u]]<h[top[v]]) swap(u,v);ColorChange(1,1,n,pos[top[u]],pos[u],w);u=fa[top[u]];}if(h[u]<h[v]) swap(u,v);ColorChange(1,1,n,pos[v],pos[u],w);
}int main()
{int a,b,c;char s[10];scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&val[i]);for(int i=1;i<n;i++){scanf("%d%d",&a,&b);g[a].push_back(b);g[b].push_back(a);}dfs1(1,0);dfs2(1,0,1);build(1,1,n);while(m--){scanf("%s",s);if(s[0]=='C'){scanf("%d%d%d",&a,&b,&c);update(a,b,c);}else if(s[0]=='Q') {scanf("%d%d",&a,&b);printf("%d\n",Findnum(a,b));}}return 0;
}
这篇关于HYSBZ - 2243染色——树链剖分+线段树建树技巧的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!