树点涂色

 

 我们定义f(x),表示x与fa[x]的颜色是否相同,相同为0,不同为1,令 f(1)=1。g(x)表示x到root路径上的f的和。然后考虑怎么维护g(x)。 

 对于操作1,用LCT中的access操作来维护。

对于操作2,直接线段树查询。

对于操作3,维护一个最大值,然后区间查询最大值就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 400010
using namespace std;
int point[N],nxt[N],v[N],tr[N],delta[N],l[N],r[N],deep[N],mi[20];
int n,m,k,col[N],f[N][20],fa[N],ch[N][3],tot,sz,pos[N],cur[N];
void add(int x,int y){
    tot++;nxt[tot]=point[x];point[x]=tot;v[tot]=y;
    tot++;nxt[tot]=point[y];point[y]=tot;v[tot]=x;
}
void dfs(int rt){
    int x=rt; 
    while (true) {
        if (!deep[x]) {
            deep[x]=deep[f[x][0]]+1; 
            for (int i=1;i<=17;i++) {
                if (deep[x]-mi[i]<0) break;
                f[x][i]=f[f[x][i-1]][i-1];
            }
            l[x]=r[x]=++sz; pos[sz]=x; cur[x]=point[x];
        }
        bool pd=false;
        for (int i=cur[x];i;i=nxt[i]) {
            cur[x]=nxt[i];
            if (v[i]!=f[x][0]) {
                f[v[i]][0]=fa[v[i]]=x; x=v[i]; pd=true;
                break;
            }
        }
        if (!pd) {
            int t=f[x][0];// cout<<t<<endl;
            r[t]=max(r[t],r[x]);
            if (x==rt)  break;
            x=t;
        }
    }
}
int lca(int x,int  y){
    if (deep[x]<deep[y]) swap(x,y);
    int k=deep[x]-deep[y];
    for (int i=0;i<=17;i++)
     if ((k>>i)&1) x=f[x][i];
    if (x==y) return x;
    for (int i=17;i>=0;i--)
     if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void update(int now){
    tr[now]=max(tr[now<<1],tr[now<<1|1]);
}
void pushdown(int now){
    if (delta[now]) {
        tr[now<<1]+=delta[now]; tr[now<<1|1]+=delta[now];
        delta[now<<1]+=delta[now]; delta[now<<1|1]+=delta[now];
        delta[now]=0;
    }
}
void change(int now,int l,int r,int ll,int rr,int val){
    if (ll<=l&&r<=rr) {
        tr[now]+=val; delta[now]+=val;
        return;
    }
    int mid=(l+r)/2; 
    pushdown(now);
    if (ll<=mid) change(now<<1,l,mid,ll,rr,val);
    if (rr>mid) change(now<<1|1,mid+1,r,ll,rr,val);
    update(now);
}
int find(int now,int l,int r,int x){
    if (l==r) return tr[now];
    int mid=(l+r)/2;
    pushdown(now);
    if (x<=mid) return find(now<<1,l,mid,x);
    else return find(now<<1|1,mid+1,r,x);
}
int query(int now,int l,int r,int ll,int rr){
    if (ll<=l&&r<=rr) return tr[now];
    int mid=(l+r)/2; int ans=0;
    pushdown(now);
    if (ll<=mid) ans=max(ans,query(now<<1,l,mid,ll,rr));
    if (rr>mid) ans=max(ans,query(now<<1|1,mid+1,r,ll,rr));
    return ans;}
bool isroot(int x){
    return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;
}
int get(int x){
    return ch[fa[x]][1]==x;
}
void rotate(int x){
    int y=fa[x]; int z=fa[y]; int which=get(x);
    if (!isroot(y)) ch[z][ch[z][1]==y]=x;
    fa[x]=z; fa[y]=x; ch[y][which]=ch[x][which^1];
    fa[ch[x][which^1]]=y; ch[x][which^1]=y;
}
void splay(int x){
    int y;
    while (!isroot(x)){
        y=fa[x];
        if (!isroot(y)) rotate(get(y)==get(x)?y:x);
        rotate(x);
    }
}
int get_root(int x){
    while (ch[x][0]) x=ch[x][0];
    return x;
}
void access(int x){
    int t=0;
    while (x) {
        col[x]=k;
        splay(x);
        int t1=get_root(ch[x][1]);
        if (t1) change(1,1,n,l[t1],r[t1],1);
        ch[x][1]=t;
        int t2=get_root(t);
        if (t2) change(1,1,n,l[t2],r[t2],-1);
        t=x; x=fa[x];
    }
}
int main(){
    scanf("%d%d",&n,&m); k=n; mi[0]=1;
    for (int i=1;i<=18;i++) mi[i]=mi[i-1]*2;
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(x,y);
    }
    dfs(1);
    for (int i=1;i<=n;i++) change(1,1,n,l[i],r[i],1);
    for (int i=1;i<=m;i++) {
        int opt,x,y; scanf("%d%d",&opt,&x);
        if (opt==1) k++,access(x);
        if (opt==2) {
            scanf("%d",&y); int t=lca(x,y);
            printf("%d\n",find(1,1,n,l[x])+find(1,1,n,l[y])-2*find(1,1,n,l[t])+1);
        }
        if (opt==3) printf("%d\n",query(1,1,n,l[x],r[x]));
    }
}

 

转载于:https://www.cnblogs.com/harden/p/6701749.html

### C语言解决图着色问题的算法实现 #### 背景介绍 图着色问题是经典的组合优化问题之一,目标是在给定约束条件下为图中的节点分配颜色。具体来说,在地图涂色问题中,相邻区域不能具有相同的颜色[^1]。 #### 算法思路 采用回溯法来解决问题是一种常见的方式。这种方法通过逐步尝试可能的颜色配置并及时剪枝不可行方案,从而有效减少计算量。为了提高效率,可以选择优先处理那些可选颜色较少的节点[^2]。 以下是基于上述原理的一个完整的C语言程序示例: ```c #include <stdio.h> #define MAXN 100 // 假设最多有100个顶点 int n, m; // n: 顶点数量;m: 边的数量 int adj[MAXN][MAXN]; // 邻接矩阵表示图 int color[MAXN]; // 存储每个顶点的颜色编号 (-1 表示未染色) int k; // 可用的最大颜色数目 // 判断第v个顶点能否被赋予color c int isSafe(int v, int c) { for (int i = 1; i <= n; ++i) { if (adj[v][i] && color[i] == c) { // 如果存在冲突,则返回false return 0; } } return 1; } // 找到下一个待涂色的顶点(优先选择剩余可用颜色最少的) int findNextVertex() { int minColors = INT_MAX, res = -1; for (int i = 1; i <= n; ++i) { if (color[i] == -1) { // 当前顶点尚未染色 int available[k]; for (int j = 0; j < k; ++j) { available[j] = 1; // 初始化所有颜色都可用 } // 更新available数组,排除邻居已使用的颜色 for (int neighbor = 1; neighbor <= n; ++neighbor) { if (adj[i][neighbor] && color[neighbor] != -1) { available[color[neighbor]] = 0; } } // 统计当前顶点可用的颜色数 int count = 0; for (int j = 0; j < k; ++j) { if (available[j]) { count++; } } // 寻找最小可用颜色数的顶点 if (count < minColors) { minColors = count; res = i; } } } return res; } // 回溯法核心函数 int solveColoringUtil() { int u = findNextVertex(); // 获取下一个需要染色的顶点 if (u == -1) { // 若无更多顶点需染色,则完成任务 return 1; } // 尝试每一种颜色 for (int c = 0; c < k; ++c) { if (isSafe(u, c)) { // 检查是否安全 color[u] = c; // 设置颜色 if (solveColoringUtil()) { // 进入下一层递归 return 1; } color[u] = -1; // 回退操作 } } return 0; // 此次尝试失败 } void graphColoring() { for (int i = 1; i <= n; ++i) { color[i] = -1; // 初始化所有顶点均未染色 } if (!solveColoringUtil()) { printf("无法使用 %d 种颜色完成染色。\n", k); } else { printf("染色结果如下:\n"); for (int i = 1; i <= n; ++i) { printf("顶点%d -> 颜色%d\n", i, color[i]); } } } int main() { scanf("%d %d", &n, &k); // 输入顶点数和最大颜色数 memset(adj, 0, sizeof(adj)); // 初始化邻接矩阵 for (int i = 0; i < m; ++i) { int a, b; scanf("%d %d", &a, &b); adj[a][b] = adj[b][a] = 1; // 添加边 } graphColoring(); return 0; } ``` 此代码实现了基本的地图涂色功能,并采用了启发式策略以提升性能[^3]。 --- #### 关键技术点解析 1. **状态空间生成** 使用回溯法构建的状态空间生成能够有效地探索所有可能性,同时利用剪枝条件剔除不必要的分支。 2. **启发式选择下一节点** 函数`findNextVertex()`负责挑选出最有可能导致快速收敛的候选节点,即剩余可用颜色最少的那个节点。 3. **安全性检测机制** `isSafe()`用于验证某特定顶点应用某一指定颜色是否会违反既定规则[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值