CF19 E Fairy——树上差分

题目:http://codeforces.com/contest/19/problem/E

先把图连成一棵树,然后对于每条非树边,判断它是在奇环中还是偶环中;

把环上的点打上相应的差分标记,并记录有多少个奇环;

dfs 出来后判断,若没有奇环,那么所有边都可以删;

若有奇环 k 个,遍历边,在 k 个奇环中而不在偶环中的边可以删;

如果一条边既在奇环中又在偶环中,那么若删掉它,原来的一奇环一偶环会合并成一个大奇环,所以这种边不能删;

写、调了整整两小时,改了许多冗余的地方和错误的地方才终于A了,我这代码能力...

码力++!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int const maxn=2e4+5;
int n,m,fa[maxn],sf[maxn],sg[maxn],g[maxn],f[maxn],ans[maxn],ns;
int col[maxn],hd[maxn],ct,cnt,qhd[maxn],numj;
bool vis[maxn];
struct N{
    int to,nxt,edge;
    N(int t=0,int n=0,int e=0):to(t),nxt(n),edge(e) {}
}ed[maxn<<1],q[maxn];
struct T{
    int ff,gg;
    T(int f=0,int g=0):ff(f),gg(g) {}
};
inline int rd()
{
    int ret=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar();
    return ret*f;
}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void add(int x,int y,int e){ed[++ct]=N(y,hd[x],e); hd[x]=ct;}
inline void ad(int x,int y,int e){q[++cnt]=N(y,qhd[x],e); qhd[x]=cnt;}
inline void dfs2(int x,int pre)
{
    vis[x]=1; col[x]=!col[pre];
    for(register int i=qhd[x];i;i=q[i].nxt)
    {
        int u=q[i].to;
        if(!vis[u])continue;
        int lca=find(u);
        if(col[u]==col[x])
        {
//            f[u]++,f[i]++,f[pr[lca]]-=2,numj++;
            f[u]++,f[x]++,f[lca]-=2,numj++;
            sf[q[i].edge]=1;
        }
        else
        {
//            g[u]++,g[i]++,g[pr[lca]]-=2,numo++;
            g[u]++,g[x]++,g[lca]-=2;
        }
    }
    for(register int i=hd[x],u;i;i=ed[i].nxt)
        if((u=ed[i].to)!=pre)dfs2(u,x);
    fa[x]=pre;
}
inline T dfs3(int x,int pre)
{
    int smf=f[x],smg=g[x]; vis[x]=1;//smf,smg!=0!!!
    for(register int i=hd[x],u;i;i=ed[i].nxt)
    {
        if((u=ed[i].to)==pre)continue;
        T k=dfs3(u,x);int e=ed[i].edge;
        sf[e]=k.ff; sg[e]=k.gg;
        smf+=k.ff; smg+=k.gg;
    }
    return T(smf,smg);
}
int main()
{
    n=rd(); m=rd();
    for(register int i=1;i<=n;i++)fa[i]=i;
    for(register int i=1,u,v;i<=m;i++)
    {
        u=rd(); v=rd();
        if(find(u)!=find(v))
        {
            fa[find(u)]=find(v);
            add(u,v,i); add(v,u,i);
        }
        else ad(u,v,i),ad(v,u,i);
    }
    for(register int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=n;i++)if(!vis[i])dfs2(i,0);//不仅dfs(1,0)
    memset(vis,0,sizeof vis);
    if(!numj)
    {
        printf("%d\n",m);
        for(int i=1;i<=m;i++)printf("%d ",i);
        return 0;
    }
    for(int i=1;i<=n;i++)if(!vis[i])dfs3(i,0);
    memset(vis,0,sizeof vis);
    for(register int i=1;i<=m;i++)
        if(sf[i]==numj&&!sg[i])ans[++ns]=i;
    printf("%d\n",ns);
    sort(ans+1,ans+ns+1);
    for(register int i=1;i<=ns;i++)printf("%d ",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/Zinn/p/9278315.html

时间: 2024-08-30 18:31:46

CF19 E Fairy——树上差分的相关文章

CF 19E Fairy——树上差分

题目:http://codeforces.com/contest/19/problem/E 去掉一条边,使无向图变成二分图. 该边应该被所有奇环经过,且不被偶环经过. 因为一条非树边一定只在一个环里.所以一条既被所有奇环经过又被偶环经过的边是树边.如果把它去掉,将无法处理包含它的那个偶环的非树边和包含它的某个奇环的非树边加上两段树边所构成的奇环. 找这样的边,弄一个边上的树上差分就行了. 可以模仿kruscal用并查集弄一个生成树.不过dfs其实也行. 注意图可能不连通. #include<io

[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)

今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分.其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过. 我们先来把树上差分能做到的看一下: 1.找所有路径公共覆盖的边 例题:[NOIP2015]运输计划 (然而我还没过就先不讲了) 反正就是中间有一步要求一条边被所有计划公共覆盖. 那么怎么求它呢?暴力(滚粗).我们有一个非常好的方法就是树上差分(记录tmp为差分数组) 询问操作为从叶子节点的权值向上累加到root 在一条路径u→ v,如果tmp[u]++,那么我

【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status][Discuss] Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui

【COGS 2434】 暗之链锁 树上差分+LCA

差分就是把一个值拆成许多差的和如 1 2 4 6 9 那么 把这个东西拆成 1 1 2 2 3 就是了,当然也可以理解为对一个问题分解为多个子问题并对其进行操作来得到原问题的答案. 树上差分就更玄妙了,它既可以把原问题拆成他到根节点的所有点,也可以拆成子树,拆成子树的话修改一个点影响的是他到根的路径上所有点,根据这个我们可以再加上LCA来解决许多问题. 这道题:I. 我们可以看出我们可以把它转化成一棵有根树,那么两部分一定是一个子树和其他 II. 那些虚边,都是砍断实边之后的藕断丝连,至于如何计

【CF739B】Alyona and a tree(树上差分,二分,树形DP)

题意:给出一棵有根树,树上每个点.每条边都有一个权值. 现在给出"控制"的定义:对一个点u,设点v在其子树上,且dis(u,v)≤av,则称u控制v. 要求求出每个点控制了多少个点 n (1?≤?n?≤?2·105).  (1?≤?ai?≤?109) 1?≤?pi?≤?n, 1?≤?wi?≤?109) 思路:在学校CF有时上不去不知道为什么 对于确定的点i,计算它对哪些点有贡献 dis[i]-dis[u]<=a[i] dis[u]<=a[i]-dis[i]满足二分性 倍增枚

XJOI CBH的发展计划(树上差分)

这是qzh的第二题 题目大意: 给你一棵树和一些连接祖先和孩子的边(非树枝边,类似于有向图返祖边) 让你求出删掉其中一条树枝边和一条非树枝边使图不联通的方案数 我们思考对树枝边统计答案 如图 对于红边统计答案,它的子树中有一条向外连的边,必须删掉红边和这条边才能使图不连通,所以这条树枝边贡献为1 如图 对于红边统计答案,它的子树中有0条向外连的边,删掉红边和任意一条虚线边能使图不连通,所以这条树枝边贡献为非树枝边的数量 如图 对于红边统计答案,它的子树中有两条及以上向外连的边,删掉红边和任意一条

BZOJ 4326 NOIP2015 运输计划 (二分+树上差分)

题意:中文题. 析:首先二分是很容易想出来的,然后主要是判断这个解合不合法,先二分答案 mid,因为有 m 个计划,所以只要添加虫洞的肯定是所有的时间长于 mid 的计划 中,也就是是那些的共同边,这个就可以用树上差分来做了,假设 s 到 t,那么让in[s]++,in[t]++,in[lca(s, t)] -= 2,其中in 表示的是 该结点与其父结点的边的计数,最后再跑一次dfs,把所有的权值都累加上去,这样就能知道哪些是共同的边了. 代码如下: #pragma comment(linker

BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分

链接 BZOJ 3631 题解 看起来是树剖?实际上树上差分就可以解决-- 当要给一条路径(u, v) +1的时候,给d[u] += 1, d[v] += 1, d[lca(u, v)] -= 1, d[fa[lca(u, v)]] -= 1. 注意这道题中路径的终点是不 +1的. #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <q

NOIp2015 运输计划 [LCA] [树上差分] [二分答案]

我太懒了 吃掉了题面 题解 & 吐槽 一道很好的树上差分练习题. 不加fread勉强a过bzoj和luogu的数据,加了fread才能在uoj里卡过去. 可以发现,答案则是运输计划里花费的最大值,最大值最小,便是二分答案的标志. 那么该怎么check呢... 我们得找出所有超过限制的计划,这个过程可以在LCA倍增的过程中预处理出来. 然后再找出一些被这些计划都覆盖的边,找到最大的那条边,如果最大的计划花费减去最大的那条边小于x,那么x就是可行的. 但是该怎么找到那些被计划都覆盖的边呢... 我们