• 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", tot);
     90         for (int j=1; j<=tot; j++) ans+=query(K-dis[j]-(len-i+1));//, printf("%lld
    ", 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
    ", ans);
    107     return 0;
    108 } 
  • 相关阅读:
    80x86的保护模式
    计算机二进制的表示
    操作系统基本知识(一)
    记录一次在安装双系统的过程(先有debian, 后加windows 8.1)
    LitePal + Gson + Volley的ORM框架尝试方案
    如何使用DDMS Heap查看Android应用内存情况
    测试驱动开发的第一个例子---我的毕业设计
    策略模式的孪生兄弟---状态模式
    面试常备---栈和队列总结篇
    面试常备题---二叉树总结篇
  • 原文地址:https://www.cnblogs.com/mzl0707/p/6189473.html
Copyright © 2020-2023  润新知