CodeForces 620E"New Year Tree"(DFS序+线段树+状态压缩)
CodeForces 620E"New Year Tree"(DFS序+线段树+状态压缩)
•题意
给你一颗 n 个节点的树,每个节点被染上了颜色;
有 m 次操作,每次操作的类型有两种
- 1 v c : 将以 v 为根的子树的结点全部涂成 c
- 2 v : 询问以 v 为根的子树的结点中不同颜色的数量
•题解
因为操作的是某个节点对应的子树,所以我们可以通过DFS序获得每一个结点的子树区间。
之后我们就将这个树形的问题转化成了一个线性区间的问题;
然后,就可以用线段树来维护(区间更新,区间查询);
求解某个区间不同颜色的个数要怎么办呢?
刚开始,我在线段树中定义了一个 $set$,存储当前区间所有的颜色;
但是,用 $set$ 超时了;
因为总共的颜色最多只有 60 种;
因此我们可以用一个范围在 $long long$ 内的二进制数 $bit$ 存储每一种颜色;
($bit$ 的二进制下的每一位代表着一种颜色)
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define ls(x) (x<<1) 5 #define rs(x) (x<<1|1) 6 #define mem(a,b) memset(a,b,sizeof(a)) 7 #define popcount(x) __builtin_popcountll(x)///获取x的二进制位1的个数 8 const int maxn=4e5+50; 9 10 int n,m; 11 int a[maxn]; 12 int num; 13 int head[maxn]; 14 struct Edge 15 { 16 int to; 17 int next; 18 }G[maxn<<1]; 19 void addEdge(int u,int v) 20 { 21 G[num]={v,head[u]}; 22 head[u]=num++; 23 } 24 struct Seg 25 { 26 int l,r; 27 ll sum; 28 int lazy; 29 int mid(){return l+((r-l)>>1);}; 30 void Set(int col) 31 { 32 sum=1ll<<col; 33 lazy=col; 34 } 35 }seg[maxn<<2]; 36 37 ///[s[u],e[u]]区间表示以u为根节点的子树的所有节点所在的区间 38 int s[maxn]; 39 int e[maxn]; 40 vector<int >vs; 41 void DFS(int u,int f) 42 { 43 vs.push_back(u); 44 s[u]=vs.size()-1; 45 for(int i=head[u];~i;i=G[i].next) 46 { 47 int v=G[i].to; 48 if(v != f) 49 DFS(v,u); 50 } 51 e[u]=vs.size()-1; 52 } 53 void pushUp(int pos) 54 { 55 seg[pos].sum=seg[ls(pos)].sum|seg[rs(pos)].sum; 56 } 57 void pushDown(int pos) 58 { 59 int &lazy=seg[pos].lazy; 60 if(lazy == -1) 61 return ; 62 63 seg[ls(pos)].Set(lazy); 64 seg[rs(pos)].Set(lazy); 65 66 lazy=-1; 67 } 68 void build(int l,int r,int pos) 69 { 70 seg[pos]={l,r}; 71 seg[pos].lazy=-1; 72 73 if(l == r) 74 { 75 seg[pos].sum=1ll<<a[vs[l]]; 76 return ; 77 } 78 79 int mid=seg[pos].mid(); 80 build(l,mid,ls(pos)); 81 build(mid+1,r,rs(pos)); 82 83 pushUp(pos); 84 } 85 void update(int pos,int l,int r,int col) 86 { 87 if(seg[pos].l == l && seg[pos].r == r) 88 { 89 seg[pos].Set(col); 90 return ; 91 } 92 pushDown(pos); 93 94 int mid=seg[pos].mid(); 95 if(r <= mid) 96 update(ls(pos),l,r,col); 97 else if(l > mid) 98 update(rs(pos),l,r,col); 99 else 100 { 101 update(ls(pos),l,mid,col); 102 update(rs(pos),mid+1,r,col); 103 } 104 pushUp(pos); 105 } 106 ll query(int pos,int l,int r) 107 { 108 if(seg[pos].l == l && seg[pos].r == r) 109 return seg[pos].sum; 110 pushDown(pos); 111 112 int mid=seg[pos].mid(); 113 if(r <= mid) 114 return query(ls(pos),l,r); 115 else if(l > mid) 116 return query(rs(pos),l,r); 117 else 118 return query(ls(pos),l,mid)|query(rs(pos),mid+1,r); 119 } 120 void Solve() 121 { 122 vs.clear(); 123 DFS(1,1); 124 build(0,vs.size()-1,1); 125 126 while(m--) 127 { 128 int op; 129 scanf("%d",&op); 130 if(op == 1) 131 { 132 int v,c; 133 scanf("%d%d",&v,&c); 134 update(1,s[v],e[v],c);///更新v节点对应的子树节点区间[s[v],e[v]]的颜色 135 } 136 else 137 { 138 int v; 139 scanf("%d",&v); 140 ll ans=query(1,s[v],e[v]); 141 printf("%d\n",popcount(ans)); 142 } 143 } 144 } 145 void Init() 146 { 147 num=0; 148 mem(head,-1); 149 } 150 int main() 151 { 152 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\C++WorkSpace\\in&&out\\contest","r",stdin); 153 Init(); 154 scanf("%d%d",&n,&m); 155 for(int i=1;i <= n;++i) 156 scanf("%d",a+i); 157 for(int i=1;i < n;++i) 158 { 159 int u,v; 160 scanf("%d%d",&u,&v); 161 addEdge(u,v); 162 addEdge(v,u); 163 } 164 Solve(); 165 166 return 0; 167 }