1. 程式人生 > >【BZOJ3648】寢室管理 樹分治

【BZOJ3648】寢室管理 樹分治

數組 沒有 != 決定 算法 microsoft put ron 1+n

【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;
}

【BZOJ3648】寢室管理 樹分治