题意



大家的考场做法

对每个点维护子树所能达到的dfn最大值、最小值、次大值、次小值,然后就可以计算原树中每个点与父亲的连边对答案的贡献。

  • 如果子树中没有边能脱离子树,断掉该边与任意一条新加的边都成立,答案就加m。
  • 如果子树中只有1条边能脱离子树,只能断掉该边和那条能脱离子树的边,答案就加1。
  • 如果子树中有大于等于2条边能脱离子树,那么不能通过断边使子树独立,答案不变。

时间复杂度\(O(n+m)\)

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
data=10*data+ch-'0',ch=getchar();
return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff; const int MAXN=3e5+7;
int n,m; struct Edge
{
int nx,to;
}E[MAXN<<2];
int head[MAXN],ecnt; void addedge(int x,int y)
{
E[++ecnt].to=y;
E[ecnt].nx=head[x],head[x]=ecnt;
} int fa[MAXN],siz[MAXN];
int dfn[MAXN],clk; void dfs1(int x,int f)
{
fa[x]=f,siz[x]=1;
dfn[x]=++clk;
for(int i=head[x];i;i=E[i].nx)
{
int y=E[i].to;
if(y==f)
continue;
dfs1(y,x);
siz[x]+=siz[y];
}
} int minv[MAXN],semi[MAXN];
int maxv[MAXN],semx[MAXN];
ll ans; void dfs2(int x)
{
for(int i=head[x];i;i=E[i].nx)
{
int y=E[i].to;
if(y==fa[x])
continue;
dfs2(y);
semi[x]=min(semi[x],max(minv[x],minv[y]));
semi[x]=min(semi[x],semi[y]);
minv[x]=min(minv[x],minv[y]);
semx[x]=max(semx[x],min(maxv[x],maxv[y]));
semx[x]=max(semx[x],semx[y]);
maxv[x]=max(maxv[x],maxv[y]);
}
int out=(minv[x]<dfn[x])+(semi[x]<dfn[x])*10+
(maxv[x]>dfn[x]+siz[x]-1)+(semx[x]>dfn[x]+siz[x]-1)*10;
/* cerr<<"check "<<x<<endl;
cerr<<" minv="<<minv[x]<<" semi="<<semi[x]<<endl;
cerr<<" maxv="<<maxv[x]<<" semx="<<semx[x]<<endl;
cerr<<" ans="<<((out==0)*m+(out==1)*1)<<endl;*/
if(x!=1)
ans+=(out==0)*m+(out==1)*1;
} int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);read(m);
for(int i=1;i<n;++i)
{
static int x,y;
read(x);read(y);
addedge(x,y);
addedge(y,x);
}
dfs1(1,0);
for(int i=1;i<=n;++i)
{
// cerr<<"dfn["<<i<<"]="<<dfn[i]<<endl;
minv[i]=maxv[i]=semi[i]=semx[i]=dfn[i];
}
for(int i=1;i<=m;++i)
{
static int x,y;
read(x);read(y);
if(dfn[x]>dfn[y])
swap(x,y);
if(dfn[x]<semi[y]) // edit 1:The first and the second must be updated like this.
{
semi[y]=dfn[x];
if(semi[y]<minv[y])
swap(semi[y],minv[y]);
}
if(dfn[y]>semx[x])
{
semx[x]=dfn[y];
if(semx[x]>maxv[x])
swap(semx[x],maxv[x]);
}
}
dfs2(1);
printf("%lld",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}

Hint

更新最大最小值以及次大次小值的时候,分为两种情况。

  1. 子树中的合并,就用吉司机线段树那种合并方式。
  2. 新增边时候的修改,必须用swap形式的方式修改。

有同学写vector存图被卡了,另外我的代码最慢测试点只跑了0.2s,以后要借鉴这种常数小的写法。

标解

我们发现如果一条树边有贡献则树上最多只有一条新加的边覆盖了它,那么我们只要写一下LCA(最近公共祖先算法)然后树上差分一下,被新边覆盖了恰好一次的树边就会有1 的贡献,而没有被覆盖的有m 的贡献,直接统计一下就好了。

复杂度:\(O(n log n)\) 或\(O(n)\)(取决于 LCA 算法的复杂度),期望得分:90~100分。

//waz
#include <bits/stdc++.h> using namespace std; #define mp make_pair
#define pb push_back
#define fi first
#define se second
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) ((int)((x).size())) typedef pair<int, int> PII;
typedef vector<int> VI;
typedef long long int64;
typedef unsigned int uint;
typedef unsigned long long uint64; #define gi(x) ((x) = F())
#define gii(x, y) (gi(x), gi(y))
#define giii(x, y, z) (gii(x, y), gi(z)) int F()
{
char ch;
int x, a;
while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-');
if (ch == '-') ch = getchar(), a = -1;
else a = 1;
x = ch - '0';
while (ch = getchar(), ch >= '0' && ch <= '9')
x = (x << 1) + (x << 3) + ch - '0';
return a * x;
} const int N = 3e5 + 10; int n, m; struct edge
{
int to;
edge *next;
} e[N << 1], *et = e, *la[N]; void add(int u, int v)
{
*++et = (edge) {v, la[u]}, la[u] = et;
*++et = (edge) {u, la[v]}, la[v] = et;
} int siz[N], son[N], fa[N], dep[N]; void dfs1(int u)
{
siz[u] = 1;
dep[u] = dep[fa[u]] + 1;
for (edge *it = la[u]; it; it = it -> next)
{
int v = it -> to;
if (v == fa[u]) continue;
fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if (siz[son[u]] < siz[v])
son[u] = v;
}
} int top[N]; void dfs2(int u, int tp)
{
top[u] = tp;
if (son[u]) dfs2(son[u], tp);
for (edge *it = la[u]; it; it = it -> next)
{
if (it -> to == son[u] || it -> to == fa[u]) continue;
dfs2(it -> to, it -> to);
}
} int lca(int u, int v)
{
for (; top[u] != top[v]; dep[top[u]] > dep[top[v]] ? u = fa[top[u]] : v = fa[top[v]]);
return dep[u] < dep[v] ? u : v;
} int cover[N]; void dfs3(int u)
{
for (edge *it = la[u]; it; it = it -> next)
{
if (it -> to == fa[u]) continue;
dfs3(it -> to);
cover[u] += cover[it -> to];
}
} int main()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
gii(n, m);
for (int i = 1; i < n; ++i)
{
int u, v;
gii(u, v);
add(u, v);
}
dfs1(1);
dfs2(1, 1);
for (int i = 1; i <= m; ++i)
{
int s, t;
gii(s, t);
++cover[s], ++cover[t];
cover[lca(s, t)] -= 2;
}
dfs3(1);
long long ans = 0;
for (int i = 2; i <= n; ++i)
if (cover[i] == 1) ++ans;
else if (!cover[i]) ans += m;
printf("%lld\n", ans);
return 0;
}

test20181004 苹果树的更多相关文章

  1. codevs 1228 苹果树 树链剖分讲解

    题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上 ...

  2. AC日记——苹果树 codevs 1228

    1228 苹果树  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解  查看运行结果     题目描述 Description 在卡卡的房子外面,有一棵 ...

  3. BZOJ 3757: 苹果树

    3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1726  Solved: 550[Submit][Status][Discuss] ...

  4. codevs1228 苹果树

    题目描述 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结出很多的苹果.卡卡非常喜欢吃苹果,所以他一直都精心的呵护这棵苹果树.我们知道树是有很多分叉点的,苹果会长在枝条的 ...

  5. 【BZOJ-3757】苹果树 块状树 + 树上莫队

    3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1305  Solved: 503[Submit][Status][Discuss] ...

  6. 洛谷P2015 二叉苹果树

    题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...

  7. 【BZOJ】【3757】苹果树

    树分块 orz HZWER http://hzwer.com/5259.html 不知为何我原本写的倍增求LCA给WA了……学习了HZWER的倍增新姿势- 树上分块的转移看vfk博客的讲解吧……(其实 ...

  8. CJOJ 1976 二叉苹果树 / URAL 1018 Binary Apple Tree(树型动态规划)

    CJOJ 1976 二叉苹果树 / URAL 1018 Binary Apple Tree(树型动态规划) Description 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的 ...

  9. 【洛谷2015】【CJOJ1976】二叉苹果树

    题面 Description 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1.我们用一根树枝两端连 ...

随机推荐

  1. 用Rails.5.2+ Vue.js做 vue-todolist app

    Rails5.2+Vue.js完成Lists(curd) 注意: Edit/update使用SPA(single-page Application单页面程序)的方法完成.点击文字出现一个输入框和按钮. ...

  2. PHP函数笔记

    一.函数(Function) 1.什么是函数:封装的,可以重复使用的                       完成特定功能的代码段. 2.分类  (1)系统函数  (2)自定义函数 3.自定义函数 ...

  3. HDOJ1004

    #include<iostream> #include "cstring" using namespace std; int add(char s1[],char s2 ...

  4. OAF 中下载使用XML Publisher下载PDF附件

    OAF doesn't readily expose the Controller Servlet's HttpRequest and HttpResponse objects so you need ...

  5. IOS UI总结

    一.UIView常见属性 1.frame  位置和尺寸(以父控件的左上角为原点(0,0)) 2.center 中点(以父控件的左上角为原点(0,0)) 3.bounds  位置和尺寸(以自己的左上角为 ...

  6. shell 命令参数

    $# 是传给脚本的参数个数$0 是脚本本身的名字$1 是传递给该shell脚本的第一个参数$2 是传递给该shell脚本的第二个参数$@ 是传给脚本的所有参数的列表$* 是以一个单字符串显示所有向脚本 ...

  7. 【webpack系列】1 What is webpack?

    什么是webpack? 现今的网页可以看做是功能丰富的应用,拥有着复杂的js代码和一大堆依赖包.为了简化开发的复杂程度,有了很多好用的实践方法 模块化 让我们可以把复杂的程序细化为小的文件 类似于Ty ...

  8. ubuntu下安装go语言;sublime+gocode搭建;go的卸载和环境变量配个人.bashrc;2空位3个网

    https://blog.csdn.net/needkane/article/details/36891949 https://www.jianshu.com/p/4f79ae4f081c http: ...

  9. win10解除密码

  10. 073——VUE中vuex之使用actions和axios异步初始购物车数据

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...