tree 1s 128M  by hzw

czy神犇种了一棵树,他想知道地球的质量

给定一棵n个点的树,求树上经过点的个数≥K的路径数量ans

对于部分数据,树上某两点间会多出最多一条无向边

输入数据

n,m,K

接下来n行,每行u,v表示u与v间有无向边连接

输出数据

ans

样例数据

input

5 5 2

1 3

2 4

3 5

4 1

5 2

output

20

数据范围

30%的数据n,m<=5000

100%的数据n,m<=100000

其中有50%的数据满足m+1=n,具体如下

测试点

前3个小数据1树2链 3环+外向树

后7个大数据4-6树 7-8环 9-10环+外向树


如果没有环,那就是经典点分治题目啦。

题目保证有且只有一个环,那么我们先随便拆掉环上的一条边,然后用点分治算出不经过这条边的满足题意的路径数量。

ans需要加上经过这条边的满足题意的路径数量。

这个怎么求呢?

看我丑丑的图。。

环可以用并查集判断(注意每次更新fa[x]不然容易暴栈),要删的边就直接不要插入。

树状数组用来判断>=x的有多少个,注意0会死循环,加个偏移量,负数就直接升到1。

代码:

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
const int N=;
int n,m,K,cx,cy,cl,tl,sl,len,has_circle;
int first[N],c[N],s[N],t[N],d[N],mark[N],size[N],f[N],cir[N];
struct node{
int x,y,next;
}a[*N];
LL ans; bool cmp(int x,int y){return x<y;} void ins(int x,int y)
{
a[++len].x=x;a[len].y=y;
a[len].next=first[x];first[x]=len;
} int findfa(int x)
{
if(f[x]==x) return x;
return f[x]=findfa(f[x]);
} void add(int x,int val)
{
x++;
for(int i=x;i>=;i-=(i&(-i))) c[i]+=val;
}
int getsum(int x)
{
x++;
if(x<=) x=;
int now=;
for(int i=x;i<=n;i+=(i&(-i))) now+=c[i];
return now;
} void find_root(int x,int fa,int tot,int &root)
{
size[x]=;
bool bk=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa || mark[y]) continue;
find_root(y,x,tot,root);
size[x]+=size[y];
if(*size[y]>tot) bk=;
}
if(bk && *(tot-size[x])<=tot) root=x;
} void DFS(int x,int fa,int tmp)
{
if(tmp)
{
d[x]=d[fa]+;
ans+=getsum(K-d[x]);
}
t[++tl]=d[x];
size[x]=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa || mark[y]) continue;
DFS(y,x,tmp);
size[x]+=size[y];
}
} void dfs(int x,int tot)
{
find_root(x,,tot,x);
sl=;s[++sl]=;add(,);
mark[x]=;d[x]=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(mark[y]) continue;
tl=;
DFS(y,x,);
for(int j=;j<=tl;j++)
{
s[++sl]=t[j];
add(t[j],);
}
}
for(int i=;i<=sl;i++) add(s[i],-);
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(mark[y]) continue;
dfs(y,size[y]);
}
} void find_circle(int x,int fa)
{
if(x==cy) {cir[++cl]=x;return ;}
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa) continue;
find_circle(y,x);
if(mark[y]) {cir[++cl]=x;mark[x]=;}
}
} void solve_circle()
{
memset(c,,sizeof(c));
memset(mark,,sizeof(mark));
mark[cx]=mark[cy]=;
find_circle(cx,); // for(int i=1;i<=cl;i++) printf("%d ",cir[i]);printf("\n");
memset(mark,,sizeof(mark));
d[]=-;tl=;
DFS(cy,,);
for(int i=;i<=tl;i++) add(t[i],);
for(int i=;i<=cl;i++) mark[cir[i]]=; K--;
for(int i=cl;i>=;i--)
{
tl=;
mark[cir[i]]=;
DFS(cir[i],,);
for(int j=;j<=tl;j++) add(t[j],-);
if(i<cl) d[cir[i]]=d[cir[i+]]+;
DFS(cir[i],cir[i+],);
mark[cir[i]]=;
}
} int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d%d%d",&n,&m,&K);K--;
int x,y,xx,yy;
ans=;len=;has_circle=;
memset(c,,sizeof(c));
memset(mark,,sizeof(mark));
memset(first,,sizeof(first));
for(int i=;i<=n;i++) f[i]=i;
for(int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
xx=findfa(x);yy=findfa(y);
if(xx==yy)
{
has_circle=;
cx=x;cy=y;
}
else
{
ins(x,y);ins(y,x);
f[xx]=yy;
}
}
// for(int i=1;i<=len;i+=2) printf("%d --> %d\n",a[i].x,a[i].y);
dfs(,n);
if(has_circle) solve_circle();
printf("%lld\n",ans);
return ;
}

【bzoj3648】环套树+点分治+树状数组的更多相关文章

  1. CF 293 E Close Vertices (树的分治+树状数组)

    转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 题目:给出一棵树,问有多少条路径权值和不大于w,长 ...

  2. hdu_4918_Query on the subtree(树的分治+树状数组)

    题目链接:hdu_4918_Query on the subtree 题意: 给出一颗n个点的树,每个点有一个权值,有两种操作,一种是将某个点的权值修改为v,另一种是查询距离点u不超过d的点的权值和. ...

  3. [bzoj3155]Preprefix sum(树状数组)

    3155: Preprefix sum Time Limit: 1 Sec  Memory Limit: 512 MBSubmit: 1183  Solved: 546[Submit][Status] ...

  4. Codeforces 980E The Number Games - 贪心 - 树状数组

    题目传送门 传送点I 传送点II 传送点III 题目大意 给定一颗有$n$个点的树,$i$号点的权值是$2^{i}$要求删去$k$个点,使得剩下的点仍然连通,并且总权值和最大,问删去的所有点的编号. ...

  5. 【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治

    3648: 寝室管理 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 239  Solved: 106[Submit][Status][Discuss] ...

  6. bzoj3648: 寝室管理(环套树+点分治)

    好题..写了两个半小时hh,省选的时候要一个半小时内调出这种题目还真是难= = 题目大意是给一棵树或环套树,求点距大于等于K的点对数 这里的树状数组做了一点变换.不是向上更新和向下求和,而是反过来,所 ...

  7. BZOJ 3648: 寝室管理( 点分治 + 树状数组 )

    1棵树的话, 点分治+你喜欢的数据结构(树状数组/线段树/平衡树)就可以秒掉, O(N log^2 N). 假如是环套树, 先去掉环上1条边, 然后O(N log^2 N)处理树(同上); 然后再O( ...

  8. hdu 4742 Pinball Game 3D 分治+树状数组

    离散化x然后用树状数组解决,排序y然后分治解决,z在分治的时候排序解决. 具体:先对y排序,solve(l,r)分成solve(l,mid),solve(mid+1,r), 然后因为是按照y排序,所以 ...

  9. BZOJ1176---[Balkan2007]Mokia (CDQ分治 + 树状数组)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1176 CDQ第一题,warush了好久.. CDQ分治推荐论文: 1 <从<C ...

随机推荐

  1. C语言 指针数组 多维数组

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21402047 . 1. 地址算数运算示例 指针算数运算 ...

  2. spring学习(一)——控制反转简单例子

    spring框架是一个开源的轻量级的基于IOC与AOP核心技术的容器框架,主要是解决企业的复杂操作实现. 那IOC与AOP,到底如何解释呢,在看spring视频中,两个专业术语一定必须要懂得. IOC ...

  3. eg_2

    2. 编写一个程序,输出在一个字符串中,指定的字符串出现的次数 第一种方法: public class Test { public static void main(String[] args) { ...

  4. SQL 单表分页存储过程和单表多字段排序和任意字段分页存储过程

      第一种:单表多字段排序分页存储过程       --支持单表多字段查询,多字段排序 create PROCEDURE [dbo].[UP_GetByPageFiledOrder] ( ), --表 ...

  5. (转)《linux性能及调优指南》 3.3 内存瓶颈

    翻译:Hank (http://blog.csdn.net/fireroll)版权所有,尊重他人劳动成果,转载时请注明作者和原始出处及本声明.原文名称:<Linux Performance an ...

  6. 《Effective C#》快速笔记(四)- 使用框架

    .NET 是一个类库,你了解的越多,自己需要编写的代码就越少. 目录 三十.使用重写而不是事件处理函数 三十一.使用 IComparable<T> 和 IComparer<T> ...

  7. [剑指Offer] 48.不用加减乘除做加法

    题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.*./四则运算符号. [思路] 首先看十进制是如何做的: 5+7=12,三步走第一步:相加各位的值,不算进位,得到2.第二步:计算进 ...

  8. Java中的缓冲流详解

    缓冲流增强了读写文件的能力,比如Student.txt是一个学生的名单,每个姓名占一行.如果我们想要读取名字,那么每次必须读取一行,使用FileReader流很难完成这样的任务,因为我们不清楚一行有多 ...

  9. 【刷题】洛谷 P3809 【模板】后缀排序

    题目背景 这是一道模板题. 题目描述 读入一个长度为 \(n\) 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置. ...

  10. 命令:ln 使用方法

    http://linux.chinaunix.net/man/2004-10-06/45.shtml 指令名称 : ln 使用权限 : 所有使用者 使用方式 : ln [options] source ...