题意



大家的考场做法

对每个点维护子树所能达到的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. 一个表单里,如果有<button>标签存在,它会自动提交表单

    可以用button代替input type=”submit”吗? 在ie下,<button>标记恐怕还存在几个不大不小的问题. 在一个表单里,如果有一个以上"submit&quo ...

  2. DownLoadImage

    Private Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA&quo ...

  3. 20170324xlVBA最简单分类计数

    Sub NextSeven_CodeFrame() Application.ScreenUpdating = False Application.DisplayAlerts = False Appli ...

  4. 20170621xlVBA跨表转换数据

    Sub 跨表转置() Dim Wb As Workbook Dim Sht As Worksheet Dim oSht As Worksheet Dim Rng As Range Dim Index ...

  5. P4721 【模板】分治 FFT

    其实是分治ntt,因为fft会爆精度,真*裸题 分治过程和fft的一模一样,主要就是ntt精度高,用原根来代替fft中的\(w_n^k\) 1.定义:设m>1,(a,m)==1,满足\(a^r= ...

  6. Java使用POI读取和写入Excel指南

    Java使用POI读取和写入Excel指南 做项目时经常有通过程序读取Excel数据,或是创建新的Excel并写入数据的需求: 网上很多经验教程里使用的POI版本都比较老了,一些API在新版里已经废弃 ...

  7. 这里面盲点很多,构造函数的调用问题,还有vptr指针的++问题(已解决)

    #include<iostream> //实现vptr指针初始化问题 using namespace std; class Father { public: Father (int f) ...

  8. thinkphp条件查询

    1.这是我在做项目的时候编写的: $profit = M('shipping_types',' ','DB_PROFIT');//没有表前缀,在M函数的第二个参数就为空. //条件$field = a ...

  9. OC MRC之多对象之间管理(代码分析)

    #import <Foundation/Foundation.h> @interface Book : NSObject { int _price; } - (void)setPrice: ...

  10. Java多线程的同步控制记录

    Java多线程的同步控制记录 一.重入锁 重入锁完全可以代替 synchronized 关键字.在JDK 1.5 早期版本,重入锁的性能优于 synchronized.JDK 1.6 开始,对于 sy ...