zoukankan      html  css  js  c++  java
  • [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           

    【题解】

        如果只是一棵树,就是一个裸的点分治模板题。

        考虑环的情况,先把环去掉变成几棵独立的树,在每棵树上跑点分治。

        现在来考虑环上的情况。

        先把环倍长,于是就变成了一条链上,每个点接了一棵树,对于链上后一半的节点,统计每个节点与之前 环长度-1 个链上节点的贡献。可以保证不重复不遗漏。

    /* --------------
        user Vanisher
        problem bzoj-3648 
    ----------------*/
    # include <bits/stdc++.h>
    # define 	N 		100010
    # define 	T		300000
    # define 	inf 	1e9
    # define 	ll 		long long
    using namespace std;
    int read(){
    	int tmp=0, fh=1; char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    	return tmp*fh;
    }
    struct node{
    	int data,next;
    }e[N*2];
    int h[T+10],low[N],dfn[N],place,head[N],ti,st[N],top,c[N],len,size[N],tag[N],mn,root,q[N],num,n,k,m;
    ll ans;
    void build(int u, int v){
    	e[++place].data=v; e[place].next=head[u]; head[u]=place;
    	e[++place].data=u; e[place].next=head[v]; head[v]=place;
    }
    int lowbit(int x){
    	return x&(-x);
    }
    int query(int x){
    	int num=0;
    	while (x>0){
    		num=num+h[x];
    		x=x-lowbit(x);
    	} 
    	return num;
    }
    void modify(int x, int num){
    	while (x<=T){
    		h[x]=h[x]+num;
    		x=x+lowbit(x);
    	}
    }
    void tarjan(int x, int fa){
    	low[x]=dfn[x]=++ti; st[++top]=x;
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (e[ed].data!=fa){
    			if (low[e[ed].data]==false){
    				tarjan(e[ed].data,x);
    				low[x]=min(low[x],low[e[ed].data]);
    			}
    			else low[x]=min(low[x],dfn[e[ed].data]);
    		}
    	if (low[x]==dfn[x]){
    		if (st[top]==x) top--;
    		else {
    			while (st[top]!=x) c[++len]=st[top--];
    			c[++len]=st[top--];
    			for (int i=1; i<=len; i++) tag[c[i]]=true;
    		}
    	}
    }
    void findroot(int x, int fa, int tot){
    	size[x]=1; int mx=0;
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (e[ed].data!=fa&&tag[e[ed].data]==false){
    			findroot(e[ed].data,x,tot);
    			mx=max(mx,size[e[ed].data]);
    			size[x]+=size[e[ed].data];
    		}
    	mx=max(tot-size[x],mx);
    	if (mn>mx) mn=mx, root=x;
    }
    void getdist(int x, int fa, int dist){
    	size[x]=1; q[++num]=dist;
    	ans=ans+query(n)-query(k-dist-1); 
    	st[++top]=dist;
    	if (dist>=k) ans++;
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (e[ed].data!=fa&&tag[e[ed].data]==false){
    			getdist(e[ed].data,x,dist+1);
    			size[x]+=size[e[ed].data];
    		}
    }
    void getdistlen(int x, int fa, int dist){
    	modify(dist,1);
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (e[ed].data!=fa&&tag[e[ed].data]==false)
    			getdistlen(e[ed].data,x,dist+1);
    }
    void del(int x, int fa, int la, int now){
    	modify(la,-1);
    	q[++num]=now;
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (e[ed].data!=fa&&tag[e[ed].data]==false)
    			del(e[ed].data,x,la+1,now+1);
    }
    void solve(int x){
    	top=0; tag[x]=true;
    	if (k==0) ans++;
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (tag[e[ed].data]==false){
    			num=0;
    			getdist(e[ed].data,0,1);
    			for (int i=1; i<=num; i++) modify(q[i],1);
    		}
    	for (int i=1; i<=top; i++) modify(st[i],-1);
    	for (int ed=head[x]; ed!=0; ed=e[ed].next)
    		if (tag[e[ed].data]==false){
    			mn=inf;
    			findroot(e[ed].data,0,size[e[ed].data]);
    			solve(root);
    		}
    }
    int main(){
    	n=read(), m=read(), k=read()-1;
    	for (int i=1; i<=m; i++){
    		int u=read(), v=read();
    		build(u,v);
    	} 
    	if (n!=m){
    		mn=inf;
    		findroot(1,0,n);
    		solve(root);
    		printf("%lld
    ",ans);
    	}
    	else{
    		tarjan(1,0);
    		for (int i=1; i<=len; i++) solve(c[i]);
    		memset(tag,0,sizeof(tag));
    		for (int i=1; i<=len; i++) tag[c[i]]=1;
    		for (int i=1; i<=len; i++)
    			getdistlen(c[i],0,2*len-i+1);
    		for (int i=len+1; i<=2*len; i++){
    			num=0;del(c[i-len],0,3*len-i+1,0);
    			for (int j=1; j<=num; j++)
    				ans=ans+query(T)-query(k-q[j]+2*len-i);
    			for (int j=1; j<=num; j++)
    				modify(2*len-i+1+q[j],1);
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }

        具体实现见代码。


  • 相关阅读:
    杭电1009 FatMouse' Trade
    【HDU 3183】 字符串处理
    quick_sort
    【 HDU 3172 Virtual Friends】 并查集+map指针优化
    【HDU 3127】 完全背包
    【URAL 1260】 DP (dfs打表之后找规律也行)
    【 HDU 3038 How Many Answers Are Wrong】 并查集好题
    腾讯云的图片上传与下载
    获取ip(局域网内的IP是一样的)
    vue表单提交之后,清空input里的数据
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136021.html
Copyright © 2011-2022 走看看