[SDOI2011] 染色 题解

题解:

先考虑在在区间上如何做这个操作。考虑两个相邻的区间A,B,不妨设区间A在区间B的左端,设区间A的颜色段数量为 $sum_A$ 区间B的颜色段数量为 $sum_B$,那么将区间A和B合并后颜色段的数量是否是 $sum_A+sum_B$ 呢?显然不是,如果区间A的右端和区间B的左端颜色相同的话,答案应该是 $sum_A+sum_B -1$,画个图很好理解。合并后的大区间的左端颜色显然和区间A的左端颜色相同,大区间的右端颜色和B的右端颜色相同。那么显然合并这个过程是满足“区间可加性”的,于是我们可以用线段树来维护某一段区间的左端颜色、右端颜色与颜色段。

那么问题来了,现在并不是在区间上,而是在树上!这时候就是我们的“树链剖分”大展身手的时刻了!树链剖分能将树分成一条一条链,且链上编号是连续的,很方便我们用线段树来维护。

附上代码(注释很详细哦):

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=200000+5;
  4 
  5 //struct数组存线段树,lson与rson代表左右区间,lco与rco代表左右两端的颜色  6 //sum代表区间颜色块的数量
  7 struct SegementTree{
  8     int lson,rson,lco,rco,sum;
  9 }t[4*N];
 10 
 11 int c[N],size[N],fa[N],son[N],d[N],top[N],id[N],color[N],cm[N];
 12 int ver[2*N],nxt[2*N],head[N];
 13 int n,m,tot,cnt;
 14 char or1;
 15 
 16 int read(){
 17     int x=0,f=1;
 18     char ch=getchar();
 19     while(ch<'0' ||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
 20     while(ch>='0' && ch<='9') {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
 21     return x*f;
 22 }
 23 
 24 void add(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
 25 
 26 void dfs1(int x){
 27     size[x]=1;d[x]=d[fa[x]]+1;
 28     for(int i=head[x];i;i=nxt[i]){
 29         int y=ver[i];
 30         if(y==fa[x]) continue;
 31         fa[y]=x;
 32         dfs1(y);
 33         size[x]+=size[y];
 34         if(size[y]>size[son[x]]) son[x]=y;
 35     }
 36 }
 37 
 38 void dfs2(int x,int tp){
 39     top[x]=tp;
 40     //给树上节点按照“重儿子”先遍历的顺序重新编号,c[x]保存的是节点x的颜色,因为要建线段树,所以用color数组按照编号来保存颜色 41     //执行一个类似"离散化"的过程
 42     id[x]=++cnt,color[cnt]=c[x]; 
 43     if(!son[x]) return;
 44     dfs2(son[x],tp);
 45     for(int i=head[x];i;i=nxt[i]){
 46         int y=ver[i];
 47         if(y==son[x] || y==fa[x]) continue;
 48         dfs2(y,y);
 49     }
 50 }
 51 
 52 //正常建线段树就好了
 53 void build(int p,int l,int r){
 54     t[p].lson=l,t[p].rson=r;
 55     if(l==r){
 56         t[p].lco=t[p].rco=color[l];//将颜色按照编号对应过来
 57         t[p].sum=1;
 58         return;
 59     }
 60     int mid=(l+r)>>1;
 61     build(p<<1,l,mid);
 62     build(p<<1|1,mid+1,r);
 63     //继承左右区间的左右两端颜色
 64     t[p].lco=t[p<<1].lco,t[p].rco=t[p<<1|1].rco;
 65     t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
 66     //如果左右区间“连接处”颜色相同的话,颜色块数量要减1
 67     if(t[p<<1].rco==t[p<<1|1].lco) t[p].sum--;
 68 }
 69 
 70 void spread(int p){//下传延迟标记
 71     if(cm[p]){
 72         t[p<<1].lco=t[p<<1].rco=cm[p];
 73         t[p<<1|1].lco=t[p<<1|1].rco=cm[p];
 74         t[p<<1].sum=t[p<<1|1].sum=1;
 75         //因为颜色是覆盖的所以不用管以前的标记
 76         cm[p<<1]=cm[p<<1|1]=cm[p];
 77         cm[p]=0;
 78     }
 79 }
 80 
 81 void changetree(int p,int l,int r,int c){
 82     if(l<=t[p].lson && r>=t[p].rson){//正常修改就行了
 83         t[p].lco=t[p].rco=c;
 84         t[p].sum=1;
 85         cm[p]=c;
 86         return;
 87     }
 88     spread(p);
 89     int mid=(t[p].lson+t[p].rson)>>1;
 90     if(l<=mid) changetree(p<<1,l,r,c);
 91     if(r>mid) changetree(p<<1|1,l,r,c);
 92     t[p].lco=t[p<<1].lco,t[p].rco=t[p<<1|1].rco;
 93     t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
 94     if(t[p<<1].rco==t[p<<1|1].lco) t[p].sum--;
 95 }
 96 
 97 void change(int a,int b,int c){
 98     while(top[a]!=top[b]){
 99         if(d[top[a]]<d[top[b]]) swap(a,b);
100         changetree(1,id[top[a]],id[a],c);//改变树链上的颜色
101         a=fa[top[a]];
102     }
103     if(d[a]>d[b]) swap(a,b);
104     changetree(1,id[a],id[b],c);
105 }
106 
107 int enqurytree(int p,int l,int r){//正常在区间上查询就行了
108     if(l<=t[p].lson && r>=t[p].rson){return t[p].sum;}
109     spread(p);
110     int mid=(t[p].lson+t[p].rson)>>1,ans=0;
111     if(l<=mid && r>mid){
112         ans=enqurytree(p<<1,l,r)+enqurytree(p<<1|1,l,r);
113         if(t[p<<1].rco==t[p<<1|1].lco) ans--;
114         return ans;
115     }else if(l>mid && r>mid){
116         ans=enqurytree(p<<1|1,l,r);
117         return ans;
118     }else if(l<=mid && r<=mid){
119         ans=enqurytree(p<<1,l,r);
120         return ans;
121     }
122 }
123 
124 int enqurytree1(int p,int x){//用来单点查询某个节点的颜色。。其实也可以用上面那个函数
125     if(t[p].lson==x && t[p].rson==x){return t[p].lco;}
126     spread(p);
127     int mid=(t[p].lson+t[p].rson)>>1;
128     if(x<=mid) return enqurytree1(p<<1,x);
129     if(x>mid) return enqurytree1(p<<1|1,x);
130 }
131 
132 int enqury(int a,int b){
133     int ans=0;
134     while(top[a]!=top[b]){
135         if(d[top[a]]<d[top[b]]) swap(a,b);
136         ans+=enqurytree(1,id[top[a]],id[a]);
137         //查询“连接处”的颜色,如果颜色相同的话那么颜色块数量要建1
138         if(enqurytree1(1,id[top[a]])==enqurytree1(1,id[fa[top[a]]])) ans--;
139         a=fa[top[a]];
140     }
141     if(d[a]>d[b]) swap(a,b);
142     //前面要减的已经减过了所以这里不用减
143     ans+=enqurytree(1,id[a],id[b]);
144     return ans;
145 }
146 
147 int main(){
148     n=read(),m=read();
149     for(int i=1;i<=n;++i) c[i]=read();
150     for(int i=1,x,y;i<n;++i){
151         x=read(),y=read();
152         add(x,y),add(y,x);
153     }
154     //处理出树链
155     dfs1(1);
156     dfs2(1,1);
157     //梦开始的地方
158     build(1,1,n);
159     while(m--){
160         scanf("%s",&or1);
161         if(or1=='C'){
162             int a,b,c;
163             a=read(),b=read(),c=read();
164             change(a,b,c);
165         }
166         if(or1=='Q'){
167             int a,b;
168             a=read(),b=read();
169             printf("%d\n",enqury(a,b));
170         }
171     }
172     return 0;
173 }

转载于:https://www.cnblogs.com/Asika3912333/p/11415883.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
06-01
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值