2024-04-09

2024-04-09

mushroom

勾石线段树题,4 个 lazy tag

考场上会了,但是写出来第二个样例一直过不去

发现每次询问全部从根 pushup 一遍就对了,于是超时拿到了 30 分的好成绩

考完试对着 aqz 的代码改了一下午,发现一模一样,一直看不出错

好心的 aqz 帮我看了看,2min 就看到我有一个 lft 写成 rgh 了(这能得 30 分就离谱,这数据还是那么水)

改完直接过了

提交记录

repair

假定 \(1\) 为根,将边权的异或和转换为点权的异或和,设 \(p_i\) 表示第 \(i\) 个点到根的路径边权的异或和

这样限制就转换为了形如 \(p_u\ \mathrm{xor}\ p_v=w\)

把限制看作是 \(u\)\(v\) 连了一条边权为 \(w\) 的边,建出一张无向图 \(G\)

发现对于 \(G\) 的每个连通块,只要我们确定了其中一个的 \(p\),就能推出连通块中所有点的 \(p\)

换句话说,如果确定连通块中某个点 \(x\)\(p\)\(p_x\),这个连通块其它点的点权可以写成 \(p_x\ \mathrm{xor}\ a\) 的形式

那么先假设 \(p_x = 0\)

dfs 推出剩下 \(p\) 值,遇到连向已经推出值的节点的边时判断是否与已经推出的值冲突,若冲突则无解

剩下的问题是如何使异或和最小

把每条边 \((i,j)\) 的边权写作 \(p_i\ \mathrm{xor}\ p_j\) 的形式

考虑 \(p_i\) 对总异或和的贡献,当且仅当 \(i\) 在树上的度数为奇数时, \(p_i\) 才产生贡献,把这样的 \(i\) 称为关键点

dfs 后已经可以求出当前的总异或和,于是想找到一种调整 \(p\) 的方法

如果把 \(G\) 中某个连通块的初始节点的 \(p\) 异或上 \(w\),根据上面的结论 \(G\) 中的每个节点的 \(p\) 都会异或 \(w\)

如果 \(G\)某个连通块的关键点数量为奇数,则这个连通块整体异或 \(w\) 会使总答案异或 \(w\)

找到这样的任意一个连通块,将其中的每个点的权值异或上当前的总异或和,这样能使总异或和为 \(0\),一定最小

如果找不到,则总异或和只能为当前值

最后通过 \(p_u,p_v\) 求出对应边权输出即可

出题人是武汉外国语的一个学姐,这是她的题解,挺清楚的,太妙了啊

这是我的代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N=2e6+10;

int n,m;
pair<int,int> es[N];
vector<pair<int,int> > G[N];
bool deg[N];
int val[N];
int grp[N];
int num,cnt;
bool flg;

void dfs(int u) {
    grp[u]=cnt;
    if(deg[u]) num++;
    for(int i=0;i<G[u].size()&&flg;i++) {
        int v=G[u][i].first,w=G[u][i].second;
        int tmp=val[u]^w;
        if(val[v]==-1) {
            val[v]=tmp;
            dfs(v);
        }
        else if(val[v]!=tmp) {
            flg=false;
            return;
        }
    }
}

int main() {
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        es[i]={u,v};
        deg[u]^=1,deg[v]^=1;
    }
    for(int i=1;i<=m;i++) {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back({v,w}),G[v].push_back({u,w});
    }
    memset(val,-1,sizeof(val));
    int ok=-1;
    for(int i=1;i<=n;i++) {
        if(val[i]==-1) {
            cnt++;
            num=0,val[i]=0;
            flg=true;
            dfs(i);
            if(!flg) {
                puts("0");
                return 0;
            }
            if(num%2==1) ok=cnt;
        }
    }
    int ans=0;
    for(int i=1;i<n;i++) {
        ans^=(val[es[i].first]^val[es[i].second]);
    }
    puts("1");
    for(int i=1;i<=n;i++) if(grp[i]==ok) val[i]^=ans;
    for(int i=1;i<n;i++) {
        int u=es[i].first,v=es[i].second;
        printf("%d ",val[u]^val[v]);
    }

    return 0;
}
posted @ 2024-04-09 21:01  OrangeStar*  阅读(6)  评论(0编辑  收藏  举报