test20181004 苹果树
题意


大家的考场做法
对每个点维护子树所能达到的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
更新最大最小值以及次大次小值的时候,分为两种情况。
- 子树中的合并,就用吉司机线段树那种合并方式。
- 新增边时候的修改,必须用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 苹果树的更多相关文章
- codevs 1228 苹果树 树链剖分讲解
题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上 ...
- AC日记——苹果树 codevs 1228
1228 苹果树 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description 在卡卡的房子外面,有一棵 ...
- BZOJ 3757: 苹果树
3757: 苹果树 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1726 Solved: 550[Submit][Status][Discuss] ...
- codevs1228 苹果树
题目描述 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结出很多的苹果.卡卡非常喜欢吃苹果,所以他一直都精心的呵护这棵苹果树.我们知道树是有很多分叉点的,苹果会长在枝条的 ...
- 【BZOJ-3757】苹果树 块状树 + 树上莫队
3757: 苹果树 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1305 Solved: 503[Submit][Status][Discuss] ...
- 洛谷P2015 二叉苹果树
题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...
- 【BZOJ】【3757】苹果树
树分块 orz HZWER http://hzwer.com/5259.html 不知为何我原本写的倍增求LCA给WA了……学习了HZWER的倍增新姿势- 树上分块的转移看vfk博客的讲解吧……(其实 ...
- CJOJ 1976 二叉苹果树 / URAL 1018 Binary Apple Tree(树型动态规划)
CJOJ 1976 二叉苹果树 / URAL 1018 Binary Apple Tree(树型动态规划) Description 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的 ...
- 【洛谷2015】【CJOJ1976】二叉苹果树
题面 Description 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1.我们用一根树枝两端连 ...
随机推荐
- C#开发框架学习
C# 开源框架(整理) Json.NET http://json.codeplex.com/ Json.Net 是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用J ...
- LeetCode--190--颠倒二进制位
问题描述: 颠倒给定的 32 位无符号整数的二进制位. 示例: 输入: 43261596 输出: 964176192 解释: 43261596 的二进制表示形式为 000000101001010000 ...
- 42 前端HTML
HTML 1. 概念 HTML 超文本标记语言(Hypertext Markup Language, HTML)是一种用于创建网页的标记语言 . 2.标签 Meta标签 <me ...
- JDBC连接SQLSERVER
package xhs;import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; im ...
- spring 监听器 IntrospectorCleanupListener
org.springframework.web.util.IntrospectorCleanupListener监听器 主要负责处理由JavaBean Introspector使用而引起的缓冲泄露, ...
- SSM框架整合(实现从数据库到页面展示)
SSM框架整合(实现从数据库到页面展示) 首先创建一个spring-web项目,然后需要配置环境dtd文件的引入,环境配置,jar包引入. 首先让我来看一下ssm的基本项目配件.(代码实现) 1.首先 ...
- Mybatis学习总结--------Mybatis <where>标签 (九)
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WH ...
- svn: E200009: 'lib/systemd/system/dropbear@.service': a peg revision is not allowed here problem
case: svn add lib/systemd/system/dropbear@.service svn: E200009: 'lib/systemd/system/dropbear@.servi ...
- Amaze UI 发布基于jQuery新版本v2.0.0之web组件
首先Amaze Ui第一版时我收到邮件邀请去试用,去了官网看了下,是基于zepto.js的一个类似bootstrap的响应式框架,提到框架当然是好事,快速开发呗.这词2.0的弃用zepto.js改用j ...
- AndroidStudio的transformDexArchiveWithExternalLibsDexMergerForDebug报错解决方案
错误排查记录. 今天在gradle更新了一个引入包的版本号,然后引发了下面的血案. 报错信息: org.gradle.api.tasks.TaskExecutionException: Executi ...