【BZOJ3648】寝室管理 树分治

【BZOJ3648】寝室管理

Description

T64有一个好朋友,叫T128。T128是寄宿生,并且最近被老师叫过去当宿管了。宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决。T128的寝室条件不是很好,所以没有很多钱来装修。礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达。最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边。但他不记得是哪条路径了。他只记得这条路径上有不少于k个寝室。于是,他想请T64帮忙数一下,有多少条这样的路径满足条件。嗯…还有一个问题。由于最近有一些熊孩子不准晚上讲话很不爽,他们决定修筑一条“情报通道”,如果通道建成,寝室就变成了一个N个点N条边的无向图。并且,经过“情报通道”的路径也是合法的。T128心想:通道建成之前,T64还有一个高效的算法帮我数路径条数,但是通道建成之后,他还有办法吗?对,T64手忙脚乱,根本数不清有多少条路径。于是他找到了你。

Input

第一行为三个正整数N,M,K(2 ≤ K ≤ N),代表有n间寝室,m条边连接它们n-1 ≤ m ≤ N;

m= n-1意味着“情报遁道”未被修好;m=n意味着“情报通道”已被修好),以及题目描述中的K。

接下来m行,每行两个正整数z,y,代表第x间寝室与第y间寝室之间有一条双向边。

Output

仅包含一个整数,代表经过至少K间寝室的路径条数。

Sample Input

5 5 2
1 3
2 4
3 5
4 1
5 2

Sample Output

20

HINT

N≤100000
K≤N
M=N

题解:现将环上的所有边都拆开,对于不经过环上的边的路径,我们可以对每个子树都用树分治解决,具体做法可以采用树形DP式+树状数组或容斥式+双指针,复杂度都是$O(nlog^2_n)$的。

对于经过环的路径,我们乱@%#^&^^搞即可。

好吧具体细节还是见代码吧。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=100010;
int f[maxn<<1],to[maxn<<1],next[maxn<<1],head[maxn],vis[maxn],s[maxn<<1],siz[maxn],tim[maxn<<1],d[maxn],p[maxn];
vector<int> ch[maxn];
int n,m,K,mn,tot,cnt,rt,now,len;
ll ans;
queue<int> q;
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
inline void updata(int x,int v)
{
	for(int i=x;i;i-=i&-i)
	{
		if(tim[i]<now)	tim[i]=now,s[i]=0;
		s[i]+=v;
	}
}
inline int query(int x)
{
	if(x<=0)	x=1;
	int ret=0,i;
	for(i=x;i<=2*n;i+=i&-i)
	{
		if(tim[i]<now)	tim[i]=now,s[i]=0;
		ret+=s[i];
	}
	return ret;
}
void getrt(int x,int fa)
{
	siz[x]=1;
	int tmp=0,i;
	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa&&!vis[to[i]])
		getrt(to[i],x),tmp=max(tmp,siz[to[i]]),siz[x]+=siz[to[i]];
	tmp=max(tmp,tot-siz[x]);
	if(tmp<mn)	mn=tmp,rt=x;
}
int getdep(int x,int fa,int dep)
{
	f[dep]++;
	int i,tmp=dep;
	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa&&!vis[to[i]])	tmp=max(tmp,getdep(to[i],x,dep+1));
	return tmp;
}
void solve(int x)
{
	vis[x]=1,now++;
	int i,j,tmp;
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])
	{
		tmp=getdep(to[i],x,1);
		for(j=1;j<=tmp;j++)	ans+=(ll)(query(K-j-1)+(j>=K-1))*f[j];
		for(j=1;j<=tmp;j++)	updata(j,f[j]),f[j]=0;
	}
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])
		mn=1<<30,tot=siz[to[i]],getrt(to[i],x),solve(rt);
}
void dfs(int x)
{
	p[++len]=x,d[x]=-2;
	for(int i=0;i<(int)ch[x].size();i++)	if(d[ch[x][i]]==-1)	dfs(ch[x][i]);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	//freopen("bz3648.in","r",stdin);
	n=rd(),m=rd(),K=rd();
	int i,j,u,a,b,tmp;
	memset(head,-1,sizeof(head));
	if(n>m)
	{
		for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
		tot=n,mn=1<<30,getrt(1,0),solve(rt);
		printf("%lld",ans);
		return 0;
	}
	for(i=1;i<=n;i++)	a=rd(),b=rd(),ch[a].push_back(b),ch[b].push_back(a),d[a]++,d[b]++;
	for(i=1;i<=n;i++)	if(d[i]==1)	q.push(i);
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=0;i<(int)ch[u].size();i++)
		{
			d[ch[u][i]]--;
			if(!d[ch[u][i]])	add(u,ch[u][i]),add(ch[u][i],u);
			if(d[ch[u][i]]==1)	q.push(ch[u][i]);
		}
	}
	for(i=1;i<=n;i++)	if(d[i]>1)	u=i,d[i]=-1;
	dfs(u);
	for(i=1;i<=len;i++)	for(j=0;j<(int)ch[p[i]].size();j++)	if(d[ch[p[i]][j]]!=-2)
		add(ch[p[i]][j],p[i]),add(p[i],ch[p[i]][j]);
	for(i=1;i<=len;i++)	tot=n,mn=1<<30,getrt(p[i],0),solve(rt);
	now++;
	memset(vis,0,sizeof(vis));
	for(i=1;i<=len;i++)
	{
		tmp=getdep(p[i],0,1);
		for(j=1;j<=tmp;j++)	ans+=(ll)query(K-(i+j)+1+n)*f[j];
		for(j=1;j<=tmp;j++)	updata(j-i+n,f[j]),f[j]=0;
	}
	now++;
	for(i=1;i<=len;i++)
	{
		tmp=getdep(p[i],0,1);
		for(j=1;j<=tmp;j++)	ans+=(ll)query(K+1-len+i-j)*f[j];
		for(j=1;j<=tmp;j++)	updata(i+j,f[j]),f[j]=0;
	}
	printf("%lld",ans);
	return 0;
}
时间: 2024-10-08 08:54:16

【BZOJ3648】寝室管理 树分治的相关文章

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

1棵树的话, 点分治+你喜欢的数据结构(树状数组/线段树/平衡树)就可以秒掉, O(N log^2 N). 假如是环套树, 先去掉环上1条边, 然后O(N log^2 N)处理树(同上); 然后再O(N log N)计算经过删掉边的路径数(在环上扫一遍, 数据结构维护). ------------------------------------------------------------------------- #include<cstdio> #include<cctype>

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

好题..写了两个半小时hh,省选的时候要一个半小时内调出这种题目还真是难= = 题目大意是给一棵树或环套树,求点距大于等于K的点对数 这里的树状数组做了一点变换.不是向上更新和向下求和,而是反过来,所以求和的时候sum(k)实际上是求k到n的和 所以我们要求大于等于k的dis的次数和,就是求sum(1,k-1),注意k要减一 如果是树,就是常规的点分治,然后用树状数组维护dis[t]出现的次数 如果是环套树,找环之后割掉一条边,然后先求这棵树的答案.接着考虑过了这条割掉的边s--t的情况:我们以

bzoj3648 寝室管理

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3648 [题解] 明天就要去ctsc/apio了有点小激动啊(题解无关 这道题如果是树就是一个裸的点分治+BIT啦! 环套树啊..去掉环上一条边,点分+BIT算一下. 然后考虑通过这条边,一定是某点环套树->边->某点环套树 一遍扫环BIT计算即可. 这样例神坑啊..少了个2 真正的样例可以看程序最后(答案没错) # include <stdio.h> # include &l

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

3648: 寝室管理 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 239  Solved: 106[Submit][Status][Discuss] Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.  T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互

『寝室管理 基环树点分』

寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了. 宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达.最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边.但他不记得是哪条路径了.他只记得这条路径上有不少于k个寝室.于是,他想请T64帮忙数一下,有多少条这样

【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态树分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,并

poj 1744 tree 树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K       Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of ve

COJ 0970 WZJ的数据结构(负三十)树分治

WZJ的数据结构(负三十) 难度级别:D: 运行时间限制:1000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 给你一棵N个点的无根树,点和边上均有权值.请你设计一个数据结构,回答M次操作. 1 x v:对于树上的每一个节点y,如果将x.y在树上的距离记为d,那么将y节点的权值加上d*v. 2 x:询问节点x的权值. 输入 第一行为一个正整数N.第二行到第N行每行三个正整数ui,vi,wi.表示一条树边从ui到vi,距离为wi.第N+1行为一个正整数M.最后

Codeforces 321C Ciel the Commander 树分治裸题

题目链接 题意: 给定一棵树,要用字母A-Z 填到每个节点上 字母可以无限使用,但A至多只能用一次 目标:对于任意两个相同字母的节点,他们之间的路径上必须有至少一个节点的字母比他们小 例如:在两个C之间至少要有一个A 或者一个B 问: 输出填涂方案. 树分治即可,最多支持2^25个节点,不会无解. #include <iostream> #include <string> #include <vector> #include <cstring> #inclu