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;
}