bzoj3648: 寝室管理(环套树+点分治)

好题。。写了两个半小时hh,省选的时候要一个半小时内调出这种题目还真是难= =

题目大意是给一棵树或环套树,求点距大于等于K的点对数

这里的树状数组做了一点变换。不是向上更新和向下求和,而是反过来,所以求和的时候sum(k)实际上是求k到n的和

所以我们要求大于等于k的dis的次数和,就是求sum(1,k-1),注意k要减一

如果是树,就是常规的点分治,然后用树状数组维护dis【t】出现的次数

如果是环套树,找环之后割掉一条边,然后先求这棵树的答案。接着考虑过了这条割掉的边s--t的情况:我们以这条边的一点t为起点,对于环上的每个点(即每棵子树的根),我们求出这棵子树的所有dis后,dis+cir_len-i为所求链的第一部分,链的第二部分的长度为k-(dis+cir_len-i),用树状数组求就可以了。更新树状数组的时候不是更新dis,而是dis+i;i即根到割的那条边的另一个点s的距离&&这条割边

完美解决。。然而常数还是很大,跑了两秒多

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<algorithm>
  4 #define INF 0x3f3f3f3f
  5 #define LL long long
  6 using namespace std;
  7 const int maxn = 100010;
  8 struct node{
  9     int to,next;
 10 }e[maxn*2];
 11 int n,m,K,head[maxn],size[maxn],vis[maxn],sz,total,root,dis[maxn],tot,fa[maxn];
 12 int ban1,ban2,cir[maxn],len=0;
 13 LL p[maxn*2],ans;
 14
 15 void insert(int u, int v){
 16     e[++tot].to=v; e[tot].next=head[u]; head[u]=tot;
 17 }
 18
 19 void add(int x, LL c){
 20     for (;x;x-=x&-x) p[x]+=c;
 21 }
 22 LL query(int x){  //注意:这里的树状数组是倒过来的, query(1,k) 是求得k+1到n
 23     LL ret=0;
 24     if (x<1) x=1;
 25     for (;x<=2*n;x+=x&-x) ret+=p[x];
 26     return ret;
 27 }
 28
 29 void getroot(int u, int f){
 30     size[u]=1; int mx=0;
 31     for (int v,i=head[u]; i; i=e[i].next){
 32         if (vis[v=e[i].to] || v==f || i==ban1 || i==ban2) continue;
 33         getroot(v,u);
 34         size[u]+=size[v];
 35         mx=max(mx,size[v]);
 36     }
 37     mx=max(mx,total-size[u]);
 38     if (mx<sz) sz=mx,root=u;
 39 }
 40
 41 void getdis(int u, int f, int d){
 42     dis[++tot]=d;
 43     for (int i=head[u],v; i; i=e[i].next){
 44         if (vis[v=e[i].to] || v==f || i==ban1 || i==ban2) continue;
 45         getdis(v,u,d+1);
 46     }
 47 }
 48
 49 void work(int u){
 50     total=size[u]?size[u]:n;
 51     sz=INF;
 52     getroot(u,0); u=root;
 53     vis[u]=1; tot=0;
 54     for (int i=head[u],v,last=0; i; i=e[i].next){
 55         if (vis[v=e[i].to] || i==ban1 || i==ban2) continue;
 56         last=tot;
 57         getdis(v,0,1); //printf("%d\n", tot);
 58         for (int j=last+1; j<=tot; j++) ans+=query(K-1-dis[j]);
 59         for (int j=last+1; j<=tot; j++) add(dis[j],1);
 60     }
 61     ans+=query(K-1);
 62     while (tot) add(dis[tot--],-1);
 63     for (int v,i=head[u]; i; i=e[i].next)
 64         if (!vis[v=e[i].to] && i!=ban1 && i!=ban2) work(v);
 65 }
 66
 67 void find_cir(int u, int f){
 68     vis[u]=1; if (len) return;//printf("  %d\n", u);
 69     for (int i=head[u],v; i; i=e[i].next){
 70         v=e[i].to;
 71         if (v==f || len) continue;
 72         fa[v]=u;// printf("now %d\n", u);
 73         if (vis[v]){
 74             ban1=i; ban2=i^1;
 75             for (int x=fa[v]; x!=v; x=fa[x]) cir[++len]=x; cir[++len]=v;
 76             return;
 77         }
 78         find_cir(v,u);
 79     }
 80 }
 81
 82 void cut(){
 83     for (int i=1; i<=n; i++) vis[i]=0;
 84     work(1);// printf("  %lld\n", ans);
 85     for (int i=0; i<=n; i++) p[i]=0LL,vis[i]=0;
 86     for (int i=1; i<=len; i++) vis[cir[i]]=1;
 87     for (int i=1; i<=len; i++){
 88         int u=cir[i]; tot=0;
 89         getdis(u,0,0); //printf("  %d\n", tot);
 90         for (int j=1; j<=tot; j++) ans+=query(K-dis[j]-(len-i+1));//, printf("%lld\n", ans);
 91         while (tot) add(dis[tot--]+i,1);
 92     }
 93 }
 94
 95 int main(){
 96     scanf("%d%d%d", &n, &m, &K); tot=1;
 97     for (int i=1,u,v; i<=m; i++){
 98         scanf("%d%d", &u, &v);
 99         insert(u,v); insert(v,u);
100     }
101     if (m==n-1) work(1);
102     else{
103         find_cir(1,0);
104         cut();
105     }
106     printf("%lld\n", ans);
107     return 0;
108 } 
时间: 2024-10-05 03:38:48

bzoj3648: 寝室管理(环套树+点分治)的相关文章

【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条双向道路连接,而且任意两间寝室之间都可以互

『寝室管理 基环树点分』

寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了. 宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达.最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边.但他不记得是哪条路径了.他只记得这条路径上有不少于k个寝室.于是,他想请T64帮忙数一下,有多少条这样

bzoj3648 寝室管理

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3648 [题解] 明天就要去ctsc/apio了有点小激动啊(题解无关 这道题如果是树就是一个裸的点分治+BIT啦! 环套树啊..去掉环上一条边,点分+BIT算一下. 然后考虑通过这条边,一定是某点环套树->边->某点环套树 一遍扫环BIT计算即可. 这样例神坑啊..少了个2 真正的样例可以看程序最后(答案没错) # include <stdio.h> # include &l

【BZOJ3648】寝室管理 树分治

[BZOJ3648]寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达.最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边.但他不记得是哪条路径了.他只记得这条路径上有不少于k个寝室.于是,他想请T64帮忙数

BZOJ 3648: 寝室管理( 点分治 + 树状数组 )

1棵树的话, 点分治+你喜欢的数据结构(树状数组/线段树/平衡树)就可以秒掉, O(N log^2 N). 假如是环套树, 先去掉环上1条边, 然后O(N log^2 N)处理树(同上); 然后再O(N log N)计算经过删掉边的路径数(在环上扫一遍, 数据结构维护). ------------------------------------------------------------------------- #include<cstdio> #include<cctype>

BZOJ 1040: [ZJOI2008]骑士 [DP 环套树]

传送门 题意:环套树的最大权独立集 一开始想处理出外向树树形$DP$然后找到环再做个环形$DP$ 然后看了看别人的题解其实只要断开环做两遍树形$DP$就行了...有道理! 然后洛谷时限再次不科学,卡常失败$SAD$ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; co

BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点结束的. 把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP. 剩下的就是这样依次统计每个环套树的直径之和. 对于环套树上找环可以借鉴最小树形图找环的技巧. 首先将边定向,保证每个点的

从零开始学建树(树的分治,树的重心)

分治算法在树的路径问题中的应用 一.树的分治算法 树的分治算法是分治思想在树型结构上的体现. 任一个具有n个节点的连通路,它的任何一棵树的树枝数为n-1 分治:除去树中的某些对象,使原树被分解成若干互不相交的部分. 分治算法分为两种:一种是点的分治,一种是边的分治 1.基于点的分治 1.选取一个点将无根树转为有根树 2.递归处理每一颗以根结点的儿子为根的子树 2.基于边的分治 1.在树中选取一条边 2. 将原有的树分成两棵不相交的树,递归处理. 可以证明在基于点的分治中,如果每次都选取树的重心,

BZOJ 1040 骑士(环套树DP)

如果m=n-1,显然这就是一个经典的树形dp. 现在是m=n,这是一个环套树森林,破掉这个环后,就成了一个树,那么这条破开的边连接的两个顶点不能同时选择.我们可以对这两个点进行两次树形DP根不选的情况. 那么答案就是每个森林的max()之和. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # in