zoukankan      html  css  js  c++  java
  • [笔记]点分治

    基本思路:点分治,是一种针对可带权树上简单路径统计问题的算法。对于一个节点,只解决经过这棵子树的根节点的路径,对于子节点问题下推子树。

    //当初的主要问题是vis[]在干什么qwq,终于知道了 
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define R register int
    using namespace std;
    #define ull unsigned long long
    #define ll long long
    #define pause (for(R i=1;i<=10000000000;++i))
    #define In freopen("NOIPAK++.in","r",stdin)
    #define Out freopen("out.out","w",stdout)
    namespace Fread {
    static char B[1<<15],*S=B,*D=B;
    #ifndef JACK
    #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++)
    #endif
    inline int g() {
      R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
      if(ch==EOF) return EOF; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
    } inline bool isempty(const char& ch) {return (ch<=36||ch>=127);}
    inline void gs(char* s) {
      register char ch; while(isempty(ch=getchar()));
      do *s++=ch; while(!isempty(ch=getchar()));
    }
    } using Fread::g; using Fread::gs;
    namespace Jack {
    const int N=10010,Inf=0x3f3f3f3f;
    int n,m,cnt,sum,rt,tot;
    int vr[N<<1],nxt[N<<1],w[N<<1],fir[N],sz[N],mx[N],d[N],a[N],b[N],q[110];
    bool ans[110],vis[N];
    inline bool cmp(int a,int b) {return d[a]<d[b];}//按路径长度排序 
    inline void add(int u,int v,int ww) {vr[++cnt]=v,nxt[cnt]=fir[u],w[cnt]=ww,fir[u]=cnt;}
    inline void getrt(int u,int fa) { sz[u]=1,mx[u]=0;//找根节点 
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(v==fa||vis[v]) continue;//若是father(vis避免扫回父亲),就continue 
        getrt(v,u); sz[u]+=sz[v];//合并子树的size 
        mx[u]=max(mx[u],sz[v]);//取max 
      } mx[u]=max(mx[u],sum-sz[u]);
      if(!rt||mx[u]<mx[rt]) rt=u;//选根节点 
    }
    inline void dfs(int u,int fa,int top) {
      a[++tot]=u;//将子树中的点添加到队列中 
      b[u]=top;//记录所属次级子树(即本次分治节点的子树)的根节点 
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(v==fa||vis[v]) continue;
        d[v]=d[u]+w[i]; dfs(v,u,top);
      }
    }
    inline void calc(int u) {//计算经过u的路径条数 
      tot=0; a[++tot]=u;//初始化队列 
      d[u]=0; b[u]=u;
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(vis[v]) continue;//不访问已经分治过的father 
        d[v]=w[i]; dfs(v,u,v);
      } sort(a+1,a+tot+1,cmp);//按到当前根的距离排序 
      for(R i=1;i<=m;++i) {
        if(ans[i]) continue;
        R l=1,r=tot; //双指针扫一遍 
        while(l<r) {
          if(d[a[l]]+d[a[r]]>q[i]) --r;//过大则左移右指针 
          else if(d[a[l]]+d[a[r]]<q[i]) ++l;//过小右移左指针 
          else if(b[a[l]]==b[a[r]]) //同属于一棵子树 
            if(d[a[r]]==d[a[r-1]]) --r;//右边权值相等左移右指针 
            else ++l; 
          else {ans[i]=true; break;}  
        }
      }
    }
    inline void solve(int u) { vis[u]=true; //已经过,打标记 
      calc(u);
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(vis[v]) continue;//vis[u],表示已经分治过的父亲。 
        sum=sz[v]; rt=0;
        getrt(v,0);
        solve(rt);//传入重心,solve 
      }
    }
    void main() {
      n=g(),m=g(); for(R i=1,u,v,w;i<n;++i) 
        u=g(),v=g(),w=g(),add(u,v,w),add(v,u,w);
      for(R i=1;i<=m;++i) q[i]=g();
      mx[rt]=sum=n;
      getrt(1,0);
      solve(rt);
      for(R i=1;i<=m;++i) 
        ans[i]?printf("AYE
    "):printf("NAY
    ");
    }
    }
    
    signed main() {
      Jack::main();
    }
    

    后来又重新学了学点分治,改了改写法。
    之前的写法其实求子树的(size)那里是不对的(就是会让下一次传进去的(sum)是错误的)
    再简单记一下思路吧:
    先在一棵树(原始的树或递归子树)中找出重心,分别对重心的每个子树进行求值(比如路径长度),然后对于求出的数据进行答案计数,然后分别递归每一颗子树。

    #include<bits/stdc++.h> 
    #define R register int
    using namespace std;
    namespace Luitaryi {
    template<class I> inline I g(I& x) { x=0; register I f=1;
      register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
      do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*=f;
    } const int N=10010,M=100,Inf=1e+9;
    int n,m,q[M],cnt,c,sum,rt,tot; bool ans[M],vis[N],mem[10000010];
    int vr[N<<1],nxt[N<<1],fir[N],w[N<<1],d[N],mx[N],sz[N],buf[N],dis[N];
    inline void add(int u,int v,int ww) {
      vr[++c]=v,nxt[c]=fir[u],w[c]=ww,fir[u]=c;
      vr[++c]=u,nxt[c]=fir[v],w[c]=ww,fir[v]=c;
    }
    inline void getsz(int u,int fa) {
      sz[u]=1,mx[u]=0; for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(vis[v]||v==fa) continue;
        getsz(v,u); sz[u]+=sz[v];
        mx[u]=max(mx[u],sz[v]);
      } mx[u]=max(mx[u],sum-sz[u]);
      if(mx[u]<mx[rt]) rt=u;
    }
    inline void getdis(int u,int fa) { dis[++cnt]=d[u]; 
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(vis[v]||v==fa) continue;
        d[v]=d[u]+w[i]; getdis(v,u);
      }
    }
    inline void solve(int u,int fa) { tot=0;
      mem[0]=true,buf[++tot]=0,vis[u]=true;
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(vis[v]||v==fa) continue;
        d[v]=w[i]; getdis(v,u);
        for(R i=1;i<=cnt;++i) 
          for(R j=1;j<=m;++j) if(q[j]>=dis[i])
            ans[j]|=mem[q[j]-dis[i]];
        for(R k=1;k<=cnt;++k) buf[++tot]=dis[k],mem[dis[k]]=true;
        cnt=0;
      } while(tot) mem[buf[tot]]=false,--tot;
      for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(vis[v]||v==fa) continue;
        sum=sz[v]; rt=0,mx[rt]=Inf;
        getsz(v,u),getsz(rt,-1);
        solve(rt,u);
      }
    }
    inline void main() {
      g(n),g(m); for(R i=1,u,v,w;i<n;++i) g(u),g(v),g(w),add(u,v,w);
      for(R i=1;i<=m;++i) g(q[i]); sum=n,rt=0,mx[rt]=Inf;
      getsz(1,-1),getsz(rt,-1); solve(rt,-1);
      for(R i=1;i<=m;++i) ans[i]?puts("AYE"):puts("NAY");
    } 
    } signed main() {Luitaryi::main(); return 0;}
    

    补一下点分治的一些题:
    Luogu P4178 Tree
    Luogu P2634 [国家集训队]聪聪可可
    Luogu P2993 [FJOI2014]最短路径树问题
    Luogu P4149 [IOI2011]Race
    Luogu P2056 [ZJOI2007]捉迷藏
    其实很多就是一些板子题,但是由于本人的代码能力过弱,思路较差,还是要赘述一番。


    2019.08.29
    71

  • 相关阅读:
    ubuntu 14.04 firefox install flash-plugin
    ubuntu node.js Binaries方式安装(二进制文件安装)
    ubuntu14.04 截图
    ubuntu 14.04下,thinkpad触摸板关闭方法
    ubuntu Mozilla Firefox install flash plugin 火狐浏览器安装flash插件
    win7+ubuntu 14.04双系统 硬盘安装
    后台启动VirtualBox虚拟机
    excel vlookup函数使用方法
    图片添加文字水印和图片水印
    记录使用Stream转多层map数据结构及遇到的坑
  • 原文地址:https://www.cnblogs.com/Jackpei/p/11431776.html
Copyright © 2011-2022 走看看