题意



大家的考场做法

对每个点维护子树所能达到的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. 3.2 x86体系结构

    计算机组成 3 指令系统体系结构 3.2 x86体系结构 X86是商业上最为成功,影响力最大的一种体系结构.但从技术的角度看,它又存在着很多的问题,那我们就来一起分析X86这种体系结构的特点. 要探讨 ...

  2. 20170714xlVba多个工作簿转多个Word文档表格

    Public Sub SameFolderGather() Application.ScreenUpdating = False Application.DisplayAlerts = False A ...

  3. 『OpenCV3』处理视频&摄像头

    在opencv中,摄像头和视频文件并没有很大不同,都是一个可以read的数据源,使用cv2.VideoCapture(path).read()可以获取(flag,当前帧),对于每一帧,使用图片处理函数 ...

  4. UVA-10692 Huge Mods

    题目大意:计算a1^a2^a3^a4......^an模m的值. 题目解析:幂取模运算的结果一定有周期.一旦找到周期就可把高次幂转化为低次幂.有降幂公式 (a^x)%m=(a^(x%phi(m)+ph ...

  5. 62. 63. Unique Paths 64. Minimum Path Sum

    1. A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). ...

  6. JAVA并行程序基础

    JAVA并行程序基础 一.有关线程你必须知道的事 进程与线程 在等待面向线程设计的计算机结构中,进程是线程的容器.我们都知道,程序是对于指令.数据及其组织形式的描述,而进程是程序的实体. 线程是轻量级 ...

  7. PHP:第三章——PHP中的回调函数

    <?php header("Content-Type:text/html;charset=utf-8"); //回调函数 //计算两个数只和 function Add($a, ...

  8. hdu 2874 Connections between cities(st&rmq LCA)

    Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (J ...

  9. EClassNotFound

    ---------------------------Debugger Exception Notification---------------------------Project Project ...

  10. 使用emma时遇到的一些问题

    今天在用使用emma的过程中遇到了几个问题,记录一下. 1.跑junit过程中没办法产生coverage data文件,导致最后没办法出emma报告,上官网查了一下原因如下: I have instr ...