首页 > 技术文章 > BSOJ5917 BZOJ3648 寝室管理

Anchor1201 2021-05-07 11:34 原文

题目

给一棵基环树,求树上路径长度 \(\ge k\) 的路径条数。

分析

点分治+基环树 \(Trick\)

首先基环树的一个基本处理办法就是先断掉一条环上的边,当成树处理后,再考虑这一条边的贡献。

这里就是必须经过这一条边的路径。

我们可以考虑这一条边的两端子树,显然就相当于拼接两条路径。

对于环上的每一个子树分开考虑,用 \(BIT\) 维护即可。

代码

实现裂开,只有 75 分。

75 分代码

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;bool f=false;char ch=getchar();
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
} 
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
const int N=2e5+5;
ll Ans;
int n,m,k;
int head[N],nex[N],to[N],idx=1;
inline void add(int u,int v){
	nex[++idx]=head[u];
	to[idx]=v;
	head[u]=idx;
	return ;
}
int Root,Size,FMax,siz[N];
bool vis[N],flag[N];

void GetRoot(int x,int fa){
	siz[x]=1;int Max=0;
	for(int i=head[x];i;i=nex[i]){
		if(flag[i]) continue;
		int y=to[i];
		if(y==fa||vis[y]) continue;
		GetRoot(y,x);siz[x]+=siz[y];
		Max=max(Max,siz[y]);
	}
	Max=max(Max,Size-siz[x]);
	if(Max<=FMax) FMax=Max,Root=x;
	return ;
}
int c[N<<2];
inline void Add(int x,int v){
	for(;x<=n*2;x+=(x&(-x))) c[x]+=v;
	return ;
}
inline int Ask(int x){
	int res=0;
	for(;x;x-=(x&(-x))) res+=c[x];
	return res;
}
int clear[N],cnt;
int Cnt,p[N];
void GetPathIn(int x,int fa,int D){
	Add(D,1),clear[++cnt]=D;
	for(int i=head[x];i;i=nex[i]){
		if(flag[i]) continue;
		int y=to[i];
		if(y==fa||vis[y]) continue;
		GetPathIn(y,x,D+1);
	}
	return ;
}
void GetPathOut(int x,int fa,int D){
	Ans+=Ask(n)-Ask(max(1,k-D-1)-1); 
	for(int i=head[x];i;i=nex[i]){
		if(flag[i]) continue;
		int y=to[i];
		if(y==fa||vis[y]) continue;
		GetPathOut(y,x,D+1);
	}
	return ;
}
void DFS(int x){
	vis[x]=true;cnt=0;
	for(int i=head[x];i;i=nex[i]){
		if(flag[i]) continue;
		int y=to[i];
		if(vis[y]) continue;
		GetPathOut(y,x,1);
		GetPathIn(y,x,1);
	} 
	Ans+=Ask(n)-Ask(k-2);//加上本身就有的路径 
	for(int i=1;i<=cnt;i++) Add(clear[i],-1);
	for(int i=head[x];i;i=nex[i]){
		if(flag[i]) continue;
		int y=to[i];
		if(vis[y]) continue;
		Root=y,FMax=Size=siz[y],GetRoot(y,x),y=Root,DFS(y);
	}vis[x]=false;
	return ;
}
int d[N],sta[N],top,top1,cl[N];
bool Insta[N];
void dfs(int x,int fa,int fr){
	if(Insta[x]){
		while(sta[top+1]!=x) cl[++top1]=sta[top--];
		return flag[fr]=flag[fr^1]=true,void();
	}
	if(vis[x]) return ;
	vis[x]=true;sta[++top]=x;Insta[x]=true;
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(y==fa) continue;
		dfs(y,x,i);
	} top--;Insta[x]=false;
	return ;
}
void Dfs1(int x,int fa,int D){
	d[++top]=D;
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(y==fa||vis[y]||flag[i]) continue;
		Dfs1(y,x,D+1);
	}
	return ;
}
void Solve(){
	memset(vis,0,sizeof(vis));
	memset(c,0,sizeof(c));
    for(int i=1;i<=top1;i++) vis[cl[i]]=true;
    for(int i=1;i<=top1;i++){
    	top=0;
        Dfs1(cl[i],0,0);
        for(int j=1;j<=top;j++) Ans+=Ask(n)-Ask(max(1,k-(top1-i+1)-d[j])-1);
        for(int j=1;j<=top;j++) Add(d[j]+i,1);
    }
	return ;
}
signed main(){
	read(n),read(m),read(k);
	for(int i=1;i<=m;i++){
		int u,v;
		read(u),read(v);
		add(u,v),add(v,u);
	}
	if(m==n){
		dfs(1,0,0);
		memset(vis,0,sizeof(vis));
		FMax=Size=n,Root=1,GetRoot(1,0),DFS(Root);
		Solve();
	}
	else FMax=Size=n,Root=1,GetRoot(1,0),DFS(Root);
	write(Ans);
	return 0;
} 
/*
5 4 2
1 3
2 4
3 5
4 1
*/

题解代码

# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 2e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, K;
int head[M], nxt[M], to[M], tot=1; bool del[M];
inline void add(int u, int v) {
    ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
}
inline void adde(int u, int v) {
    add(u, v), add(v, u);
}

ll ans = 0;

// ================== BIT =================== //
namespace BIT {
    const int M = 6e5 + 10;
    int c[M], n;
    # define lb(x) (x&(-x))
    inline void init(int _n) {
        n = _n;
        memset(c, 0, sizeof c);
    }
    inline void edt(int x, int d) {
        for (; x<=n; x+=lb(x)) c[x] += d;
    }
    inline int sum(int x) {
        int ret = 0;
        for (; x; x-=lb(x)) ret += c[x];
        return ret;
    }
    inline int sum(int x, int y) {
        return sum(y) - sum(x-1);
    }
    # undef lb
}

// ================== dfz =================== //
int sz[M], mx[M];
bool vis[M];
inline void getsz(int x, int fa=0) {
    sz[x] = 1, mx[x] = 0;
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || to[i] == fa || vis[to[i]]) continue;
        getsz(to[i], x);
        sz[x] += sz[to[i]];
        if(sz[to[i]] > mx[x]) mx[x] = sz[to[i]];
    }
}

int mi, centre;
inline void getcentre(int x, int tp, int fa=0) {
    if(sz[tp] - sz[x] > mx[x]) mx[x] = sz[tp] - sz[x];
    if(mx[x] < mi) mi = mx[x], centre = x;
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || to[i] == fa || vis[to[i]]) continue;
        getcentre(to[i], tp, x);
    }
}

inline void getans(int x, int d, int fa=0) {
    ans += BIT::sum(max(1, K-d-1), n);
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || to[i] == fa || vis[to[i]]) continue;
        getans(to[i], d+1, x);
    }
}

inline void addans(int x, int d, int fa=0) {
    BIT::edt(d, 1);
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || to[i] == fa || vis[to[i]]) continue;
        addans(to[i], d+1, x);
    }
}
    
inline void delans(int x, int d, int fa=0) {
    BIT::edt(d, -1);
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || to[i] == fa || vis[to[i]]) continue;
        delans(to[i], d+1, x);
    }
}


inline void dfz(int x) {
    getsz(x); mi = n;
    getcentre(x, x);
    x = centre;
    // do something
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || vis[to[i]]) continue;
        getans(to[i], 1, x);
        addans(to[i], 1, x);
    }
    ans += BIT::sum(K-1, n);
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || vis[to[i]]) continue;
        delans(to[i], 1, x);
    }
    vis[x] = 1;
    for (int i=head[x]; i; i=nxt[i]) {
        if(del[i] || vis[to[i]]) continue;
        dfz(to[i]);
    }
} 

inline void solve_dfz() {
    memset(vis, 0, sizeof vis);
    BIT::init(n); dfz(1);
}

int st[M], stn, c[M], cn;
inline void dfs_circle(int x, int fa=0) {
    if(cn) return;
    if(vis[x]) {
        for (int i=stn; st[i]!=x; --i) c[++cn] = st[i];
        c[++cn] = x;
        return ;
    }
    st[++stn] = x;
    vis[x] = 1; 
    for (int i=head[x]; i; i=nxt[i]) {
        if(to[i] == fa) continue;
        dfs_circle(to[i], x);
    }
    --stn;
}

int d[M], dn=0;
inline void getdis(int x, int dis, int fa=0) {
    d[++dn] = dis;
    for (int i=head[x]; i; i=nxt[i]) {
        if(to[i] == fa || del[i] || vis[to[i]]) continue;
        getdis(to[i], dis+1, x);
    }
}

inline void solve() {
    stn = cn = 0;
    dfs_circle(1);
//    printf("%d\n", cn);
//    for (int i=1; i<=cn; ++i) printf("%d ", c[i]);
//    puts("");
    for (int i=head[c[1]]; i; i=nxt[i]) {
        if(to[i] == c[cn]) {
            del[i] = 1; del[i^1] = 1;
            break;
        }
    }
    solve_dfz();
    // cross c[cn]->c[1]
//    printf("%lld\n", ans);
    BIT::init(n);
    memset(vis, 0, sizeof vis);
    for (int i=1; i<=cn; ++i) vis[c[i]] = 1;
    for (int i=1; i<=cn; ++i) {
        dn = 0;
        getdis(c[i], 0);
        for (int j=1; j<=dn; ++j) ans += BIT::sum(max(1, K-(cn-i+1)-d[j]), n);
        for (int j=1; j<=dn; ++j) BIT::edt(d[j]+i, 1);
    }
    printf("%lld\n", ans);
}

int main() {
    scanf("%d%d%d", &n, &m, &K);
    for (int i=1, u, v; i<=m; ++i) {
        scanf("%d%d", &u, &v);
        adde(u, v);
    }
    if(n == m) solve();
    else solve_dfz(), printf("%lld\n", ans);
    return 0;
}
/*
5 5 2
1 3
2 4
3 5
4 1
5 2
*/

感受

这道题相当于其他点分治多的就是基环树的那个处理套路,遇到基环树题就可以考虑这样的做法。

推荐阅读