【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条双向道路连接,而且任意两间寝室之间都可以互达。最近,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
1 3
2 4
3 5
4 1
5 2
Sample Output
HINT
N≤100000
K≤N
M=N
Source
Solution
首先考虑M=N-1的情况,路径问题,树上可以直接点分治算出
M=N的情况,就是环套树,方法是一样的,先拆环,用处理树的方法计算答案,然后单独计算经过环的答案
找环的过程,并查集即可....
统计答案,利用树状数组即可,或者Splay,线段树都可以,具体作用就是处理完一颗子树,将答案加入树状数组,再处理其他子树的时候,再加入答案进行计算,保证不出现遗漏
考虑经过环的情况,从路径上找到环,更新环上点的所有权,再进行一次计算,即可
或者,正反扫两次环亦可
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; void Freopen() {freopen("sport.in","r",stdin); freopen("sport.out","w",stdout);} int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 100010 int N,M,K; struct EdgeNode{int next,to;}edge[MAXN<<1]; int head[MAXN],cnt=1; void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);} int CA,CB,f[MAXN],visit[MAXN],mark[MAXN],size[MAXN],deep[MAXN],Size,root,from[MAXN],C[MAXN],cn; long long Ans; struct BinaryIndexedTree { int NUM,bl[MAXN],id; long long tree[MAXN]; int lowbit(int x) {return x&(-x);} void Add(int pos,int D) { for (int i=pos; i<=NUM; i+=lowbit(i)) if (bl[i]!=id) tree[i]=1,bl[i]=id; else tree[i]+=D; } void Sub(int pos,int D) { for (int i=pos; i<=NUM; i+=lowbit(i)) tree[i]+=D; } long long Query(int pos) { if (pos<0) return 0; long long re=0; for (int i=pos; i; i-=lowbit(i)) if (bl[i]==id) re+=tree[i]; return re; } long long Query(int L,int R) {return Query(R)-Query(L-1);} }bit; struct UnionFind { int Fa[MAXN]; void init() {for (int i=1; i<=N; i++) Fa[i]=i;} int find(int x) {if (Fa[x]==x) return x; else return Fa[x]=find(Fa[x]);} void merge(int x,int y) {int f1=find(x),f2=find(y); if (f1!=f2) Fa[f1]=f2;} }uf; void DFSroot(int now,int last) { f[now]=0,size[now]=1; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) { DFSroot(edge[i].to,now); size[now]+=size[edge[i].to]; f[now]=max(f[now],size[edge[i].to]); } f[now]=max(f[now],Size-size[now]); if (f[now]<f[root]) root=now; } void CalcT(int now,int last,int dep) { Ans+=bit.Query(K-dep,N); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) CalcT(edge[i].to,now,dep+1); } void AddAns(int now,int last,int dep) { bit.Add(dep+1,1); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) AddAns(edge[i].to,now,dep+1); } void Divide(int now) { visit[now]=1; bit.id++; bit.Add(1,1); for (int i=head[now]; i; i=edge[i].next) if (!visit[edge[i].to]) CalcT(edge[i].to,now,1),AddAns(edge[i].to,now,1); for (int i=head[now]; i; i=edge[i].next) if (!visit[edge[i].to]) { f[root=0]=Size=size[edge[i].to]; DFSroot(edge[i].to,now); Divide(root); } } void SolveT() { f[root=0]=N; Size=N; DFSroot(1,0); Divide(root); } void GetCircle(int now,int last,int dep) { from[now]=last; bit.Add(dep,1); deep[now]=dep; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last) GetCircle(edge[i].to,now,dep+1); } void CalcCT(int now,int last,int D) { Ans+=bit.Query(K-D,N); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !mark[edge[i].to]) CalcCT(edge[i].to,now,D+1); } void SubAns(int now,int last) { bit.Sub(deep[now],-1); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !mark[edge[i].to]) SubAns(edge[i].to,now); } void SolveCT() { bit.id++; GetCircle(CA,0,1); for (int i=CB; i; i=from[i]) C[++cn]=i,mark[i]=1; for (int i=1; i<cn; i++) SubAns(C[i],0),CalcCT(C[i],0,i); } int main() { // Freopen(); N=read(); M=N; M=read(); K=read(); uf.init(); bit.NUM=N; for (int x,y,i=1; i<=M; i++) { x=read(),y=read(); if (uf.find(x)!=uf.find(y)) InsertEdge(x,y),uf.merge(x,y); else CA=x,CB=y; } SolveT(); if (N==M) SolveCT(); printf("%lld\n",Ans); return 0; }
这里有个坑,某种大环的情况,会被卡RE....