问题 E: 造船
时间限制: 1 Sec 内存限制: 128 MB
提交 状态
题目描述
小Y想要在虚拟世界里造船。最开始n个船的完成度全部都为0。小Y第iii时刻可以在ai和bi两艘船中选择一艘让这艘船的完成度+1。
由于国家政府是奇数控,所以所有偶数完成度的船只都将被摧毁,小Y想知道m时刻后能剩下来的船只最多有多少艘。
输入
第一行两个整数n和m
接下来m行,每行两个整数ai,bi
输出
一个整数,表示m时刻后能剩下来的船只最多有多少艘。
样例输入 Copy
8 6 1 2 3 4 4 5 6 7 7 8 8 6
样例输出 Copy
6
提示
20%的数据,m≤20,n≤100
对于100%的数据,1≤n,m≤200000
题目思路:
这个题目有点秀..
首先容易想到通过这些点的链接之后,某些点是具有关联性的:(想改变一个可以改变另一个)
所以用并查集找出其分的连通块,该联通块内是具有关联性的。
连通块外对当前块的答案无影响
之后我们把题目的操作看为,对边的操作。
接下来有一个技巧:a-b连边之后 ,如果选择a,就改变该边的方向 ,即a<-b
首先肯定的结论是:
1.如果边数>=点数可以直接将所有的点都+1,这点是肯定的(图联通)
之后考虑剩下的边 :
假设a - >b之间剩下三条边
假设c->d之间剩下四条边
假设e->f之间剩下五条边
那么此时因为首先将图中点都加了1(已经确保图中的点全为奇数),我们可以选择剩下的边每次对一个连接的点+2,那么最终的状态就是 每两个相邻点之间最多剩一条边
此时再怎么分配呢?
通过画图可以发现,这个边权的加法可以传递,也就是说:假设a->b->c,如果a,b间多一条边,那么就可以让b+1,然后b-c之间已经放好的边又可以让b-1,c+1,所以相当于c+1
但是这个过程需要连续:万一是a->b<c,此时c就不能+1
所以我们可以发现所有的点不一定可以转移到同一个点上(因为这个图只有刚开始是一个环,注意上面黑字),有可能改变一次后就不在有环了,就会导致有的点的边权传递不过来。
但是!
此时可以发现,任意两个边可以传递到两边之间的任意一个点,使得中间的点+2
所以我们就可以这么考虑,把所有相邻的2个点都化成一团,这样就不会出现断开的情况(刚开始全部相连)
所以每相邻两点都可以组合,因此当剩下边是奇数时不能再组合。
所以这个集合的贡献即为 n-1或者n
2.如果当边数<点数,此时只有一种情况:树
可以发现树一定可以全部变奇数,贡献为点数
Code:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define ls k<<1
#define rs k<<1|1
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pp;
const ll INF=1e17;
const int maxn=2e5+6;
const int mod=998244353;
const double eps=1e-3;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
int sz[maxn],ed[maxn];
int pre[maxn];
int vis[maxn];
int Find(int x){
return x == pre[x]?x:pre[x] = Find(pre[x]);
}
int main(){
read(n);read(m);
for(int i=1;i<=n;i++) pre[i] = i,sz[i] = 1;
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
int dx = Find(x),dy = Find(y);
if(dx != dy){
pre[dx] = dy;
sz[dy] += sz[dx];
ed[dy] += ed[dx]+1;
}
if(dx == dy) ed[dx]++;
}
ll ans = 0;
for(int i=1;i<=n;i++){
int dx = Find(i);
if(!vis[dx]){
if(ed[dx]-sz[dx]<0) ans += sz[dx]-1;
else if((ed[dx]-sz[dx])&1) ans += sz[dx]-1;
else ans += sz[dx];
vis[dx] = 1;
}
}
printf("%lld\n",ans);
return 0;
}