题目大意
% 对于 n n n 个数,初始值为零,共有 q q q 组操作,形如 ( u , v ) ( u,v) (u,v),你需要在 u u u 和 v v v 种选择一个数,将以这个数为下标的位置加一,使得最终奇数个数尽量多,输出最多的个数。
题解
%
本题仅仅要求奇数尽量多,因而两次操作相当于没有操作,这让我们想到了异或操作。更进一步,如果我们对于每个无序二元组
(
u
,
v
)
(u,v)
(u,v),先钦定
u
u
u 作为加一的对象,那么如果后面我们需要反悔,只需要将
u
u
u 和
v
v
v 的权值均异或上一即可。
这样考虑似乎很难找到多个二元组之间的关系,我们考虑将这些二元关系建成一张图,
∀
(
u
,
v
)
\forall \ (u,v)
∀ (u,v),我们建立一张图
G
=
{
V
,
E
}
G=\{V,E\}
G={V,E} 有
(
u
,
v
)
∈
E
(u,v)\in E
(u,v)∈E1,点权为这个数是否为奇数。
我们仍然按照之前的说法,对于一条边,钦定一个端点异或上1,可以发现,对于一个连通块,其中任意两个权值为零的点均可以通过给某些边的两个端点异或上1的方法,将这两个权值为零的点的权值均变为1。
于是我们得到了一个完整的算法,先建图,钦定任意一个端点,将其点权异或一,然后计算出每个连通块中点权为1的点数
a
a
a,点权为零的点数
b
b
b,则该连通块最多能有
⌊
a
+
b
2
⌋
\lfloor\frac {a+b}2\rfloor
⌊2a+b⌋ 个点的点权为1,也就是答案。
代码
% 代码非常好打。
#include<bits/stdc++.h>
using namespace std;
#define maxn 200010
struct edge{
int v,next;
}edges[maxn<<1];
int head[maxn],cnte;
void ins(int u,int v){
edges[++cnte]=(edge){v,head[u]};
head[u]=cnte;
}
int d[maxn],vis[maxn];
void dfs(int u,int fa,int &c0,int &c1){
if(vis[u]) return;
vis[u]=true;
c1+=d[u];c0+=!d[u];
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(v==fa) continue;
dfs(v,u,c0,c1);
}
}
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);d[u]^=1;
if(u!=v) ins(u,v),ins(v,u);
} int ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
int c0=0,c1=0;
dfs(i,-1,c0,c1);
if(c0&1) ans+=(c0-1)+c1;
else ans+=c0+c1;
}
} printf("%d\n",ans);
return 0;
}
建立一张图,对于每个二元组 ( u , v ) (u,v) (u,v),在图中连边 ( u , v ) (u,v) (u,v)。 ↩︎