算法学社
記錄難忘的征途
posts - 141,comments - 220,trackbacks - 0

题目描述

   在一颗点数为N<100,000的树上,每个点有一个颜色。请你实现两种操作 1. 给一段路径u->v染色 2. 询问路径u->v上有多少种颜色

吐槽:

    1. 4KB的代码...
    2. 唉.... 这题的思路非常好想... 但是我还是捉了好久....真弱...
    3. 我尽量把代码写的清楚了...

算法分析:

    1. 基于树的动态统计,还是要先做树链剖分
    2. 至于线段怎么维护是个问题,这里我的方法是: 线段树记录每个区间的颜色段数,和最左段与最右段的颜色。更新的话要用到懒惰标记法。
    3. 比维护/查询边要好写吧....

  1
 // figo 5.16.2012
  2 // template & head file
  3 #include<cassert>
  4 #include<iostream>
  5 #include<cstdio>
  6 #include<cstring>
  7 using namespace std;
  8 template <typename T> inline void chkmin(T &a,const T b) { if(a > b) a = b;}
  9 template <typename T> inline void chkmax(T &a,const T b) { if(a < b) a = b;}
 10 // graph
 11 const int V = 100005;
 12 const int E = V * 2;
 13 int head[V],pnt[E],nxt[E],color[V],e,n;
 14 void add_edge(int u,int v){
 15     nxt[e] = head[u];
 16     head[u] = e;
 17     pnt[e] = v;
 18     e ++;
 19 }
 20 // segment tree
 21 int LT,RT;
 22 int lt[V<<2], rt[V<<2] ,clr[V<<2], cover[V<<2];
 23 void pushdown(int pos,int L,int R){
 24     if(L == R){ return;}
 25     if(cover[pos] == 1) {
 26         cover[pos]= 0;
 27         clr[pos<<1] = clr[pos<<1|1] = 1;
 28         lt[pos<<1] = rt[pos<<1] = lt[pos<<1|1] = rt[pos<<1|1] = lt[pos];
 29         cover[pos<<1] = cover[pos<<1|1] = 1;
 30     }
 31 }
 32 void upt(int pos,int L,int R){
 33     if(L==R) return;
 34     lt[pos] = lt[pos << 1];
 35     rt[pos] = rt[pos << 1 | 1];
 36     clr[pos] = clr[pos<<1] + clr[pos<<1|1] - (rt[pos<<1]==lt[pos<<1|1]);
 37 }
 38 int __ans,__last;
 39 void update(int l,int r,int pos,int L,int R,char cmd,int c = 0){
 40     if(L >= l && R <= r){
 41         if(cmd == 'C'){
 42             cover[pos] = clr[pos] = 1;
 43             lt[pos] = rt[pos] = c;
 44         }
 45         if(L == l) LT = lt[pos];
 46         if(R == r) RT = rt[pos];
 47         __ans += clr[pos] - (__last==lt[pos]);
 48         __last = rt[pos];
 49         return;
 50     }
 51     pushdown(pos,L,R);
 52     int mid = L + R >> 1;
 53     if(mid >= l) update(l,r,pos << 1, L, mid,cmd, c);
 54     if(mid < r) update(l,r,pos<<1|1, mid+1,R,cmd, c);
 55     upt(pos,L,R);
 56 }
 57 // dsu
 58 int parent[V];
 59 int find(int u){
 60     return u == parent[u] ? u : parent[u] = find(parent[u]);
 61 }
 62 // divide and conquer
 63 const int inf = ~0u>>2;
 64 int sz[V],P[V],heavy[V],deep[V],flag[V];
 65 int dfs(int u,int f){
 66     int mx = -1, s = -1;
 67     sz[u] = 1;
 68     for(int i = head[u];i!=-1; i = nxt[i]){
 69         int v = pnt[i];
 70         if(v == f) continue;
 71         deep[v] = deep[u] + 1;
 72         P[v] = u;
 73         dfs(v,u);
 74         sz[u] += sz[v];
 75         if(sz[v] > mx){
 76             mx = sz[v]; s = v;
 77         }
 78     }
 79     heavy[u] = s;
 80     if(s >= 0) parent[s] = u;
 81 }
 82 int prepare(){
 83     for(int i =0; i<n; i++) parent[i] = i;
 84     deep[0] = 0;P[0] = -1;
 85     dfs(0,0);
 86     int segsz = 0;
 87     for(int i=0; i<n; i++) if(heavy[i] == -1){
 88         int v = i;
 89         while(v && heavy[P[v]] == v){
 90             update(segsz,segsz,1,0,n,'C',color[v]);
 91             flag[v] = segsz ++;
 92             v = P[v];
 93         }
 94         update(segsz,segsz,1,0,n,'C',color[v]);
 95         flag[v] = segsz ++;
 96     }
 97     assert(segsz==n);
 98 }
 99 // operator
100 int lca(int u,int v){
101     while(1){
102         int U = find(u);
103         int V = find(v);
104         if(U == V) return deep[u] < deep[v] ? u : v;
105         else if(deep[U] >= deep[V])
106             u = P[U];
107         else v = P[V];
108     }
109 }
110 int query(int u,int p,char cmd,int c=0){
111     int ans = 0;
112     int last = -1;
113     while(u != P[p]){
114         int l = flag[u];
115         int v = find(u);
116         if(deep[v]<deep[p]) v = p;
117         int r = flag[v];
118         __ans =0; __last = -1;
119         update(l,r,1,0,n,cmd,c);
120         ans += __ans - (LT == last);
121         last = RT;
122         u = P[v];
123     }
124     return ans;
125 }
126 // main
127 int ask(int u,int v,char cmd,int c=0){
128     int p = lca(u,v);
129     return query(u,p,cmd,c) + query(v,p,cmd,c) - 1;
130 }
131 int main(){
132     int m,u,v;
133     scanf("%d%d",&n,&m);
134     e = 0;
135     for(int i=0;i<n;i++) head[i] = -1;
136     for(int i=0;i<n;i++) scanf("%d",&color[i]);
137     for(int i=1;i<n;i++){
138         scanf("%d%d",&u,&v);
139         u--; v--;
140         add_edge(u,v);
141         add_edge(v,u);
142     }
143     prepare();
144     char cmd[2];int c=0;
145     while(m--){
146         scanf("%s%d%d",cmd,&u,&v);
147         u--; v--;
148         if(cmd[0]=='C') scanf("%d",&c);
149         int ans = ask(u,v,cmd[0],c);
150         if(cmd[0]=='Q') printf("%d\n",ans);
151     }
152 }
posted on 2012-05-16 17:10 西月弦 阅读(857) 评论(1)  编辑 收藏 引用 所属分类: 解题报告

FeedBack:
# re: bzoj 2243 树链剖分+线段树
2013-02-10 12:05 | silver__bullet
树链剖分之后,如果两个点在树的路径中是连续的,而映射到线段树上之后这两个点不连续,对于这种情况那怎么维护?  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理