zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    runtime iOS 运行时机制
    iOS 文件操作
    responseCode 状态吗查询
    iOS常用宏定义
    Block里用self造成循环引用
    iOS Block全面分析
    OC与Swift混编
    iOS打包app发给测试人员测试
    Swift UITextField
    sqilite学习
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7787193.html
Copyright © 2011-2022 走看看