bzoj4238-Loj2881-JOISC2014 电压【树上差分】
题目解析
如果对每一条边都尝试染色的话,复杂度显然不可过。
考虑从宏观上大局考虑。(这句话怎么这么别扭,是我语文太菜了
如果图中有奇环,那么奇环上的边必须被选,否则如果选了其它的边,奇环上的边就一定有一条左右端点颜色一样。
同样考虑偶环,我们发现偶环上的边必须不被选,否则如果选了偶环上的边,那么偶环上一定有一条边左右端点颜色一样。
总结一下,就是:
如果没有奇环,那么偶环以外的边可以随便选。
如果有奇环,那么能选的边就是所有奇环上的边的交集,并且不在偶环上。
不过,在偶环上的边一定不在奇环上,因为如果一条边既在偶环上,又在奇环上,那通过这条边相连的奇环和偶环可以组成一个更大的奇环(可以画图看一下),而这条边不在这个更大的奇环上。(当然,我的做法判不了这个更大的奇环,所以需要判偶环)
算了,还是画个图:(这个图可能要先看一下下面的具体做法再来看
(可以看到从树上下下来长这样,可以更直观地看到\(2-3\)边一定不在大奇环\(1-2-5-4-3-1\)上
可以用\(dfs\)序解决这个问题,如果搜到一条返祖边就说明成环了,而我们可以通过\(Δdep\)判断是偶环还是奇环,然后树上差分就可以算出这条边在多少个奇环/偶环上。
最后统计答案找到所有奇环覆盖数\(==\)奇环总数,并且不在偶环上的边的总数。
注意一下,我们在算答案的时候是没有考虑返祖边的,因为如果有\(2\)个以上的奇环,那么这两条返祖边都不在一起,肯定不在交集里面。但是,如果只有一个奇环的话,这条返祖边是可行的,所以答案要\(+1\)。
最后再注意一下程序写法,是差分边,不是差分点,注意一下细节。
►Code View
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define M 200005
#define MOD 998244353
#define INF 0x3f3f3f3f
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
return f*x;
}
struct node{
int nxt,v;
}e[M<<1];
int hd[N],cnt=1;
void add(int u,int v)
{
e[++cnt].nxt=hd[u],e[cnt].v=v;
hd[u]=cnt;
}
int n,m;
bool vis[N],rt[N];
int dep[N],sum[N][2]/*差分数组 标记这个点头上的那条边 注意是边*/,tot/*奇环个数*/;
int f[N];//记录这个点头上的边的编号
void dfs(int u)
{
vis[u]=1;
for(int i=hd[u];i;i=e[i].nxt)
{
int v=e[i].v;
//if(v==fa) continue;
if(i==f[u]||(i^1)==f[u]) continue;
if(vis[v])//返祖边
{
if(dep[v]>dep[u]) continue;//走到了返祖边的反向边
int d=(dep[u]-dep[v])&1;
sum[u][d]++,sum[v][d]--;
if(!d) tot++;
}
else
{
f[v]=i;
dep[v]=dep[u]+1;
dfs(v);
sum[u][0]+=sum[v][0],sum[u][1]+=sum[v][1];//差分合并
}
}
}
int main()
{
n=rd(),m=rd();
for(int i=1;i<=m;i++)
{
int u=rd(),v=rd();
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
if(!vis[i])
{
rt[i]=1;
dfs(i);
}
int ans=0;
//printf("%d\n",tot);
//for(int i=1;i<=n;i++)
// printf("%d\n",sum[i][1]);
for(int i=1;i<=n;i++)
if(sum[i][0]==tot&&!sum[i][1]&&!rt[i])//在所有奇环上 不在任何偶环上 并且不是根(根头顶上没有边
ans++;
if(tot==1) ans++;//只有一个奇环 那么那条返祖边也要算上
printf("%d\n",ans);
return 0;
}
/*
因为这道题是按边算 可能会有重边 所以只能写前向星 按边来
主要是v==fa那里的判断 不能判断是不是父节点 而要判是不是当初来的那条边或它的反向边
样例2就可以帮助理解这种情况
*/
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com