【BZOJ2243】【SDOI2011】染色

题意见试题传送门

解题思路:显然是题树剖题。

考虑用线段树维护区间端点颜色与颜色数,这样就可以方便的合并,注意查询的时候对端点的特殊处理即可。

时间效率最高为\( O (m \log^{2} n) \).(BZOJ 上 4072ms)

#include <stdio.h>
#define MN 100005
#define Mn (1<<17)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mid (l+r>>1)
int lc[Mn<<1],rc[Mn<<1],sum[Mn<<1],mark[Mn<<1];
int n,q,col[MN],siz[MN],fa[MN],son[MN],dep[MN],top[MN],pos[MN],head[MN],cnt,dfsn,rev[MN];
int to[MN<<1],nxt[MN<<1];
inline int in(){
    int x=0;bool f=0; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘) f=ch==‘-‘,ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar();
    return f?-x:x;
}
inline void swp(int &a,int &b){a^=b^=a^=b;}
inline void ins(int x,int y){to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;}
inline void dfs1(int u,int f,int d){
    fa[u]=f,dep[u]=d,siz[u]=1;
    for (register int i=head[u]; i; i=nxt[i])
        if (to[i]!=f){
            dfs1(to[i],u,d+1);siz[u]+=siz[to[i]];
            if (siz[to[i]]>siz[son[u]]) son[u]=to[i];
        }
}
inline void dfs2(int u,int tp){
    top[u]=tp;pos[u]=(++dfsn);rev[dfsn]=u;if (son[u]) dfs2(son[u],tp);
    for (register int i=head[u]; i; i=nxt[i])
        if (to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i]);
}
inline void pushdown(int k){
    mark[ls(k)]=mark[rs(k)]=lc[ls(k)]=rc[ls(k)]=lc[rs(k)]=rc[rs(k)]=mark[k];
    sum[ls(k)]=sum[rs(k)]=1;mark[k]=0;
}
inline void combine(int k){
    sum[k]=rc[ls(k)]==lc[rs(k)]?sum[ls(k)]+sum[rs(k)]-1:sum[ls(k)]+sum[rs(k)];
    lc[k]=lc[ls(k)],rc[k]=rc[rs(k)];
}
inline void build(int k,int l,int r){
    mark[k]=0;
    if (l==r){sum[k]=1,lc[k]=rc[k]=col[rev[l]];return;}
    build(ls(k),l,mid);build(rs(k),mid+1,r);combine(k);
}
inline void A(int l,int r,int a,int b,int k,int col){
    if (a<=l&&r<=b){mark[k]=lc[k]=rc[k]=col;sum[k]=1;return;}
    if (mark[k]) pushdown(k);if (a<=mid) A(l,mid,a,b,ls(k),col);
    if (b>mid) A(mid+1,r,a,b,rs(k),col);combine(k);
}
inline int Qs(int l,int r,int a,int b,int k){
    if (l==a&&r==b) return sum[k];if (mark[k]) pushdown(k);
    if (b<=mid) return Qs(l,mid,a,b,ls(k));
    if (a>mid) return Qs(mid+1,r,a,b,rs(k));
    return Qs(l,mid,a,mid,ls(k))+Qs(mid+1,r,mid+1,b,rs(k))-(rc[ls(k)]==lc[rs(k)]);
}
inline int Qc(int l,int r,int x,int k){
    if (l==r) return lc[k];if (mark[k]) pushdown(k);
    if (x<=mid) return Qc(l,mid,x,ls(k));return Qc(mid+1,r,x,rs(k));
}
inline void update(int x,int y,int cl){
    while (top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) swp(x,y);
        A(1,n,pos[top[x]],pos[x],1,cl);x=fa[top[x]];
    }if (dep[x]>dep[y]) swp(x,y);A(1,n,pos[x],pos[y],1,cl);
}
inline int query(int x,int y){
    register int res=0;
    while(top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) swp(x,y);
        res+=Qs(1,n,pos[top[x]],pos[x],1)-(Qc(1,n,pos[top[x]],1)==Qc(1,n,pos[fa[top[x]]],1));
        x=fa[top[x]];
    }if (dep[x]>dep[y]) swp(x,y);res+=Qs(1,n,pos[x],pos[y],1);return res;
}
void init(){
    n=in(),q=in();for (int i=1; i<=n; ++i) col[i]=in();
    for (register int i=1; i<n; ++i){
        register int x=in(),y=in();
        ins(x,y); ins(y,x);
    }dfs1(1,1,1);dfs2(1,1);build(1,1,n);
}
void solve(){
    while(q--){
        register char ch=getchar();while (ch!=‘C‘&&ch!=‘Q‘) ch=getchar();
        register int x=in(),y=in();
        if (ch==‘C‘) update(x,y,in());
        else printf("%d\n",query(x,y));
    }
}
int main(){init(); solve(); return 0;}
时间: 2024-08-06 19:47:34

【BZOJ2243】【SDOI2011】染色的相关文章

bzoj2243 [SDOI2011]染色 动态树

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 110000 int pre[N],ch[N][2]; int e[N],ne[N*2],v[N*2]; int nn,m; int col[N]; int lc[N],sm[N],rc[N],num[N]; int rt[N],n; int qu[N

BZOJ2243 [SDOI2011]染色

题意:树,路径染色,路径查询分了几段. 分析: 树链剖分套线段树,没写过,代码写得很乱,还犯了不少错,加了点注释,以后不能犯这种错了. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define m ((L+R)>>1) #define lc o<<1 #define rc o<<1|1 #define ls lc,L,m

[BZOJ2243][SDOI2011]染色 解题报告|树链剖分

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. 与上一题差别不大,主要就是solve过程要根据左端点和右端点的颜色处理合并时候的情况 线段树的每个节点要记录颜色段数|最左边的颜色|最右边的颜色 同时往下传的时候标记要做好(之前那道题是单点修改所以不用考虑

BZOJ2243 [SDOI2011]染色(树链剖分+线段树合并)

题目链接 BZOJ2243 树链剖分+线段树合并 线段树合并的一些细节需要注意一下 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 100010; int n, m,

bzoj2243: [SDOI2011]染色 树链剖分

裸树剖. #include<bits/stdc++.h> using namespace std; #define N 100010 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define L l,M,P #define R M+1,r,S #define Z int l=1,int r=n,int k=1 typedef int ds[N]; ds dp,num,p,size,son,t

bzoj2243 sdoi2011 染色 paint

明明是裸树剖 竟然调了这么久好蛋疼 大概是自己比较水的原因吧 顺便+fastio来gangbang #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cstdio> #include<cctype> using namespace std; const int Maxn=100010,Maxm=Maxn; int n,

[BZOJ2243]SDOI2011染色|树链剖分|LCT

裸题嘛.. 先考虑一条线段上如何查询颜色段数,只要对每个线段树节点多维护一个左颜色和右颜色,然后合并的时候sum[x]=sum[lc]+sum[rc]-(左儿子的右颜色==右儿子的左颜色)..实在太久没写树剖结果码+调试花了两节多晚自习,,各种傻逼错误,什么反向边忘加,标记忘记下传...还有就是更新答案的时候,关键的一点是要保证当前的两点(也就是a,b)是没有被更新到的,否则很难搞.. 表示LCT要更好写..不过在BZOJ上我的树链剖分6000+MS,LCT要13000+MS.. 树链剖分: #

【树链剖分】bzoj2243 [SDOI2011]染色

树链剖分模板题.线段树维护每个段中的颜色数.左端点颜色.右端点颜色. pushup: col[rt]=col[rt<<1]+col[rt<<1|1]-(Rcol[rt<<1]==Lcol[rt<<1|1]); 在合并各个链的答案时,要注意若两头颜色相同,ans--. [教训:树链剖分题在进行建立线段树树时,要注意下面代码中的标注部分.否则会WA] Code: 1 #include<cstdio> 2 #include<algorithm&g

bzoj2243: [SDOI2011]染色--线段树+树链剖分

此题代码量较大..但是打起来很爽 原本不用lca做一直wa不知道为什么.. 后来改lca重打了一遍= =结果一遍就AC了orz 题目比较裸,也挺容易打,主要是因为思路可以比较清晰 另:加读入优化比没加快了1.3s.. 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 100010; 6 struct node{

[BZOJ2243][SDOI2011]染色(树链剖分)

[传送门] 树链剖分就行了,注意线段树上颜色的合并 Code #include <cstdio> #include <algorithm> #define N 100010 #define MID int mid=(l+r)>>1,ls=id<<1,rs=id<<1|1 #define len (r-l+1) using namespace std; struct tree{ int lc,rc,sum,tag; tree(){lc=rc=tag