BZOJ3648 寝室管理 【点分治 + 环套树】
3648: 寝室管理
Time Limit: 40 Sec Memory Limit: 512 MB
Submit: 366 Solved: 152
[Submit][Status][Discuss]
Description
Input
Output
仅包含一个整数,代表经过至少K间寝室的路径条数。
Sample Input
1 3
2 4
3 5
4 1
5 2
Sample Output
HINT
N≤100000
K≤N
M=N
如果是一棵树,就是普通的点分治,对于u为根,统计每棵子树的深度并用树状数组统计相加大于等于K的答案
如果加上一个环,我们用tarjan算法找出环,拆掉其中一条边,就可以跑一遍点分治算出不经过该边的方案
再来就考虑经过该边,朝该边的一个方向逐个访问环上各点
对于点u,先统计其外向树的深度,再与u之前的点的外向树维护的树状数组,计算跨过拆掉的边所形成的答案
如图:
每到一个外向树,先统计顺时针走到断边后之前有多少点可以满足相加>=K,统计完后再将自己逆时针走到断边的长度记入树状数组
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #define LL long long int
- #define REP(i,n) for (int i = 1; i <= (n); i++)
- #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
- #define lbt(x) (x & -x)
- using namespace std;
- const int maxn = 100005,maxm = 200005,INF = 1000000000;
- inline int RD(){
- int out = 0,flag = 1; char c = getchar();
- while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
- while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
- return out * flag;
- }
- int N,M,K,h[maxn],ne = 0;
- int F[maxn],rt,Sum,Siz[maxn],vis[maxn];
- int d[maxn],t[maxn],n,A[maxn];
- int cir[maxn],ciri = 0,dfn[maxn],st[maxn],top = 0,cnt = 0,sta[maxn],tp = 0;
- LL ans = 0;
- struct EDGE{int to,nxt,v;}ed[maxm];
- inline void build(int u,int v){
- ed[ne] = (EDGE){v,h[u],1}; h[u] = ne++;
- ed[ne] = (EDGE){u,h[v],1}; h[v] = ne++;
- }
- inline void add(int u,int v){while (u <= N) A[u] += v,u += lbt(u);}
- inline int query(int u){int ans = 0; while (u) ans += A[u],u -= lbt(u); return ans;}
- inline int sum(int l,int r){return query(r) - query(l - 1);}
- void getRT(int u,int f){
- int to; F[u] = 0; Siz[u] = 1;
- Redge(u) if (ed[k].v && (to = ed[k].to) != f && !vis[to]){
- getRT(to,u); F[u] = max(F[u],Siz[to]); Siz[u] += Siz[to];
- }
- F[u] = max(F[u],Sum - Siz[u]);
- if (F[u] < F[rt]) rt = u;
- }
- void dfs(int u,int f){
- t[++n] = u; int to;
- Redge(u) if (ed[k].v && !vis[to = ed[k].to] && to != f)
- d[to] = d[u] + 1,dfs(to,u);
- }
- void solve(int u){
- vis[u] = true; int to;
- add(1,1);
- Redge(u) if (ed[k].v && !vis[to = ed[k].to]){
- n = 0; d[to] = 2; dfs(to,u);
- REP(i,n) ans += sum(max(1,K - d[t[i]] + 1),N);
- REP(i,n) add(d[t[i]],1);
- }
- Redge(u) if (ed[k].v && !vis[to = ed[k].to]){
- n = 0; d[to] = 2; dfs(to,u);
- REP(i,n) add(d[t[i]],-1);
- }
- add(1,-1);
- Redge(u) if (ed[k].v && !vis[to = ed[k].to]){
- Sum = Siz[to]; F[rt = 0] = INF; getRT(to,u);
- solve(rt);
- }
- }
- void dfs1(int u,int pre){
- dfn[u] = ++cnt; st[++top] = sta[++tp] = u; int to;
- Redge(u){
- if (k == pre) continue;
- if (!dfn[to = ed[k].to]) dfs1(to,k ^ 1);
- else while (dfn[st[top]] > dfn[to]) top--;
- }
- if (st[top] == u){
- if (ciri > 1) return;
- ciri = 0; top--;
- do { cir[++ciri] = sta[tp]; } while (sta[tp--] != u);
- }
- }
- void solve1(){
- F[rt = 0] = INF; Sum = N; getRT(1,0);
- solve(rt);
- cout<<ans<<endl;
- }
- void solve2(){
- dfs1(1,-1);
- Redge(cir[1]) if (ed[k].to == cir[ciri]) {ed[k].v = ed[k ^ 1].v = 0; break;}
- F[rt = 0] = INF; Sum = N; getRT(1,0);
- solve(rt);
- memset(vis,false,sizeof(vis));
- for (int i = 1; i <= ciri; i++){
- int u = cir[i],len = ciri - i;
- n = 0; vis[cir[i - 1]] = vis[cir[i + 1]] = true; d[u] = 1;
- dfs(u,0);
- vis[cir[i - 1]] = vis[cir[i + 1]] = false;
- REP(j,n) ans += sum(max(1,K - d[t[j]] - len),N);
- REP(j,n) add(d[t[j]] + i - 1,1);
- }
- cout<<ans<<endl;
- }
- int main(){
- memset(h,-1,sizeof(h));
- N = RD(); M = RD(); K = RD();
- REP(i,M) build(RD(),RD());
- if (M < N) solve1();
- else solve2();
- return 0;
- }
BZOJ3648 寝室管理 【点分治 + 环套树】的更多相关文章
- bzoj3648: 寝室管理(环套树+点分治)
好题..写了两个半小时hh,省选的时候要一个半小时内调出这种题目还真是难= = 题目大意是给一棵树或环套树,求点距大于等于K的点对数 这里的树状数组做了一点变换.不是向上更新和向下求和,而是反过来,所 ...
- BZOJ 3648: 寝室管理( 点分治 + 树状数组 )
1棵树的话, 点分治+你喜欢的数据结构(树状数组/线段树/平衡树)就可以秒掉, O(N log^2 N). 假如是环套树, 先去掉环上1条边, 然后O(N log^2 N)处理树(同上); 然后再O( ...
- BZOJ3648 : 寝室管理
求环套外向树上节点数不小于K的路径数. 首先树的话直接点分治+树状数组$O(n\log^2n)$搞定 环套树的话,先删掉多余的边(a,b) 然后变成了一棵树,直接点分治 然后在树上找到a到b的路径,将 ...
- 【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治
3648: 寝室管理 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 239 Solved: 106[Submit][Status][Discuss] ...
- 【BZOJ3648】寝室管理 树分治
[BZOJ3648]寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T6 ...
- [ARC083F] Collecting Balls [建二分图+环套树定向+建拓扑图+树的拓扑序计数]
题面 [传送门](https://arc083.contest.atcoder.jp/tasks/arc083_d) 思路 这是一道真正的好题 第一步:转化模型 行列支配类的问题,常见做法就是把行和列 ...
- 【BZOJ-1040】骑士 树形DP + 环套树 + DFS
1040: [ZJOI2008]骑士 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3312 Solved: 1269[Submit][Status ...
- 【BZOJ】1040: [ZJOI2008]骑士(环套树dp)
http://www.lydsy.com/JudgeOnline/problem.php?id=1040 简直不能再神的题orz. 蒟蒻即使蒟蒻,完全不会. 一开始看到数据n<=1000000就 ...
- UVALive 7148 LRIP【树分治+线段树】
题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...
随机推荐
- Hadoop(5)-Hive
在Hadoop的存储处理方面提供了两种不同的机制,一种是之前介绍过的Hbase,另外一种就是Hive,有关于Hbase,它是一种nosql数据库的一种,是一种数据库,基于分布式的列式存储,适合海量数据 ...
- hdu6370 并查集+dfs
Werewolf Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total ...
- 5 多线程 模拟qq聊天
1.多线程思路 使用多线程完成一个全双工的QQ聊天程序 2.版本1:程序小框架 #1.收数据,然后打印 def recvData(): pass #2.检测键盘,发数据 def sendData(): ...
- P2347 砝码称重
P2347 砝码称重 题目描述 设有1g.2g.3g.5g.10g.20g的砝码各若干枚(其总重<=1000), 输入输出格式 输入格式: 输入方式:a1 a2 a3 a4 a5 a6 (表示1 ...
- nodejs的交叉(跨平台)编译(to android)
nodejs的二进制包有两种安装方式node-gyp以及node-pre-gyp 这两条命令会写入该包的安装脚本. node-gyp是使用gyp工具编译源码,因此必须指定交叉编译器(参见http:// ...
- 用链表实现nodejs的内存对象管理
虽然javascript拥有垃圾收集,但是垃圾收集机制并不会自动释放持久对象,比如websocks连接. 为了能够在某些特定情况下中止一些连接(比如内存不足),显然要建立全局的对象管理器进行管理. 显 ...
- SVN脱离锁定的几种方法
SVN经常出现被锁定而无法提交的问题,选择解锁又提示没有文件被锁定,很是头疼.这里整理了一下SVN 被锁定的几种解决方法: 1.出现这个问题后使用“清理”即"Clean up"功能 ...
- Python 套接字的使用 (1)
获取设备名称和IPv4地址 socket.gethostname() socket.gethostbyname(host_name) def print_machine_info(): host_ ...
- Mysql性能优化三:主从配置,读写分离
大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够.到了数据业务层.数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢 ...
- Word2Vec词向量(一)
一.词向量基础(一)来源背景 word2vec是google在2013年推出的一个NLP工具,它的特点是将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系.虽然源码是 ...