[SDOI2011]染色(信息学奥赛一本通 1563)(洛谷 2486)

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数n和m,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面行每行包含两个整数x和y,表示xy之间有一条无向边。

下面行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

Output

对于每个询问操作,输出一行答案。

Sample Input

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

Sample Output

3
1
2

HINT

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。


--->题目是复制过来的,感觉字体大小很奇怪啊...不管了,先把这道鸽了我好几天的题A掉

这道题呢主要是运用树链剖分线段树的思想

我们用tree这个结构体来维护线段树中每个节点的左端点的颜色lc、右端点的颜色rc、区间标记col(别忘了标记下传)以及该区间颜色段的数量sum。

以下几点需要特别注意(本题难点):

  • 在区间合并的时候,如果左子树的右端右子树的左端颜色一致,那么该区间sum--;

  • 在剖链的时候,如果上一条链的左端(即靠近根节点的一端,因为在建树的时候,线段树中越左边的位置越靠近根节点)颜色和当前剖到的链的右段(即远离根节点的一端,两链相交处)颜色一致,那么总颜色数sum--;

  • top[x]==top[y],即两点已经在同一条重链上时,两边端点的颜色都应与对应的点进行比较,如颜色一致,同样总颜色数sum--;

详情请见代码:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1e6+5,inf=0x3f3f3f3f;
  4 int next[2*N],head[2*N],to[N*2],d[N],fa[N],v[2*N],seg[4*N],rev[4*N],top[N],size[N],son[N],w[N];
  5 int n;
  6 int m,q,p,tot,cnt;
  7 int sum,ma;
  8 struct node{
  9     int lc,rc,sum,col;
 10 }tree[N];
 11 void ADD(int k,int l,int r,int v)
 12 {
 13     tree[k].lc=tree[k].rc=v;
 14     tree[k].sum=1;
 15     tree[k].col=v;
 16     return ;
 17 }
 18 void pushdown(int k,int l,int r,int mid)
 19 {
 20     if(!tree[k].col)return ;
 21     ADD(k<<1,l,mid,tree[k].col);
 22     ADD(k<<1|1,mid+1,r,tree[k].col);
 23     tree[k].col=0;
 24 }
 25 void dfs1(int s,int ff)
 26 {
 27     size[s]=1;d[s]=d[ff]+1;fa[s]=ff;
 28     for(int i=head[s],t;i,t=to[i];i=next[i])
 29     {
 30         if(t!=fa[s])
 31         {
 32             dfs1(t,s);
 33             size[s]+=size[t];
 34             if(size[t]>size[son[s]])son[s]=t;
 35         }
 36     }
 37 }
 38 void dfs2(int s)
 39 {
 40     if(son[s])
 41     {
 42         seg[son[s]]=++seg[0];
 43         top[son[s]]=top[s];
 44         rev[seg[0]]=son[s];
 45         dfs2(son[s]);
 46     }
 47     for(int i=head[s],t;t=to[i],i;i=next[i])
 48     {
 49         if(!top[t])
 50         {
 51             seg[t]=++seg[0];
 52             rev[seg[0]]=t;
 53             top[t]=t;
 54             dfs2(t);
 55         }
 56     }
 57 }
 58 void update(int k)
 59 {
 60     tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
 61     if(tree[k<<1].rc==tree[k<<1|1].lc)tree[k].sum--;
 62     tree[k].lc=tree[k<<1].lc;
 63     tree[k].rc=tree[k<<1|1].rc;
 64 }
 65 void build(int k,int l,int r)
 66 {
 67     if(l==r)
 68     {
 69         tree[k].lc=tree[k].rc=w[rev[l]];
 70         tree[k].sum=1;
 71         return ;
 72     }
 73     int mid=l+r>>1;
 74     build(k<<1,l,mid);
 75     build(k<<1|1,mid+1,r);
 76     update(k);
 77 }
 78 node query2(int k,int l,int r,int x,int y)
 79 {
 80     if(l>=x&&r<=y)
 81     {
 82         return tree[k];
 83     }
 84     int mid=l+r>>1;
 85     pushdown(k,l,r,mid);
 86     if(mid>=x)
 87     {
 88         if(mid<y)
 89         {
 90             node res,ll=query2(k<<1,l,mid,x,y),rr=query2(k<<1|1,mid+1,r,x,y);
 91             res.sum=ll.sum+rr.sum+(ll.rc==rr.lc?-1:0);
 92             res.lc=ll.lc;res.rc=rr.rc;
 93             return res;
 94         }
 95         else return query2(k<<1,l,mid,x,y);
 96     }
 97     else return query2(k<<1|1,mid+1,r,x,y);
 98 }
 99 int query1(int x,int y)
100 {
101     //这里可能不是很好理解,但是把a、b当做现任和备胎可能会好想一些(机房大佬lhy指点) 
102     int sum=0,a=0,b=0;
103     int fx=top[x],fy=top[y];
104     while(fx!=fy)
105     {
106         if(d[fx]<d[fy])swap(x,y),swap(fx,fy),swap(a,b);
107         node res=query2(1,1,n,seg[fx],seg[x]);
108         sum+=res.sum;
109         if(res.rc==a)sum--;
110         a=res.lc;
111         x=fa[fx];fx=top[x];
112     }
113     if(d[x]>d[y])swap(x,y),swap(a,b);
114     node res=query2(1,1,n,seg[x],seg[y]);
115     sum+=res.sum;
116     if(res.lc==a) sum--;
117     if(res.rc==b) sum--;
118     return sum;
119 }
120 int read()
121 {
122     int f=1;char ch;
123     while((ch=getchar())<'0'||ch>'9')
124         if(ch=='-')f=-1;
125     int res=ch-'0';
126     while((ch=getchar())>='0'&&ch<='9')
127         res=res*10+ch-'0';
128     return res*f;
129 }
130 void write(int x)
131 {
132     if(x<0)
133     {
134         putchar('-');
135         x=-x;
136     }
137     if(x>9)write(x/10);
138     putchar(x%10+'0');
139 }
140 void add(int x,int y)
141 {
142     next[++tot]=head[x];
143     head[x]=tot;
144     to[tot]=y;
145 }
146 void change2(int k,int l,int r,int x,int y,int v)
147 {
148     if(l>=x&&r<=y)return ADD(k,l,r,v);
149     int mid=l+r>>1;
150     pushdown(k,l,r,mid);
151     if(mid>=x)change2(k<<1,l,mid,x,y,v);
152     if(mid<y)change2(k<<1|1,mid+1,r,x,y,v);
153     update(k);
154 }
155 void change1(int x,int y,int v)
156 {
157     int fx=top[x],fy=top[y];
158     while(fx!=fy)
159     {
160         if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
161         change2(1,1,n,seg[fx],seg[x],v);
162         x=fa[fx];fx=top[x];
163     }
164     if(d[x]>d[y])swap(x,y);
165     change2(1,1,n,seg[x],seg[y],v);
166 }
167 int main()
168 {
169     n=read();m=read();
170     for(int i=1;i<=n;i++)w[i]=read();
171     for(int i=1;i<n;i++)
172     {
173         int x,y;
174         x=read();y=read();
175         add(x,y);add(y,x);
176     }
177     dfs1(1,0);
178     seg[0]=top[1]=rev[1]=seg[1]=1;
179     dfs2(1);
180     build(1,1,n);
181     while(m--)
182     {
183         char ch;int a,b;
184         while((ch=getchar())<'A'||ch>'Z');
185         //这个地方读入一定要小心!我的爆零经验就是前车之鉴!!! 
186         a=read();b=read();
187         if(ch=='C')
188         {
189             int c;c=read();
190             change1(a,b,c);
191         }
192         else
193         {
194             write(query1(a,b));
195             putchar('\n');
196         }
197     }
198     return 0;
199 }
200 //硬生生凑个200行 ~(~ ̄▽ ̄)~

转载于:https://www.cnblogs.com/ljy-endl/p/11375638.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值