1. 程式人生 > >模板·點分治(luogu P3806)

模板·點分治(luogu P3806)

new etc [] 變量 HR Go gist fin freopen

[模板]洛谷·點分治


1、求樹的重心

樹的重心:若A點的子樹中最大的子樹的size[] 最小時,A為該樹的中心

步驟:

  • 所需變量:siz[x] 表示 x 的子樹大小(含自己),msz[x] 表示 其子樹中最大的子樹的大小,sum表示當前子樹所有節點個數,root表示當前子樹根節點
  • 處理出siz[x],msz[x]
  • 按最大子樹最小的標準處理出root
inline void GetRoot(int x,int fa){
    siz[x]=1;msz[x]=0;siz[x]//表示 x 的子樹大小(含自己),msz[x] 表示其子樹中最大的子樹的大小   
    for(Rint i=beg[x];i;i=nex[i]){
        int
y=to[i]; if(y==fa||vis[y])continue; GetRoot(y,x); siz[x]+=siz[y]; msz[x]=max(msz[x],siz[y]); } msz[x]=max(msz[x],sum-siz[x]);//按最大子樹最小的標準處理出root if(msz[x]<msz[root])root=x; } inline void GR_Init(int x,int fa){ sum=siz[x];//sum表示當前子樹所有節點個數 root=0
;//root表示當前子樹根節點 GetRoot(x,fa); }

2、分而治之

步驟:

  • 處理出經過這個節點的所有所求貢獻
  • 減去其子樹內不需要被計算但卻被計算的貢獻
  • 對其子樹繼續分治
inline void Point_Divide(int x){
    vis[x]=1;
    Calc(x,0,1);//處理出經過這個節點的所有所求貢獻
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(vis[y])continue;
        Calc(y,w[i],-1);//減去其子樹內不需要被計算但卻被計算的貢獻
GR_Init(y,x); Point_Divide(root);//對其子樹繼續分治 } }

3、洛谷P3806點分治模板AC代碼

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
using namespace std;
typedef long long LL;
template<typename T>inline void read(T &x){
    x=0;T w=1,ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
    x=x*w;
}
inline void File(){
    freopen("fuck.in","r",stdin);
    freopen("fuck.out","w",stdout);
}

const int maxn=100000+10,inf=0x7f7f7f7f;
int n,m;
int e,beg[maxn],nex[maxn<<1],to[maxn<<1],w[maxn<<1];
int dis[maxn],siz[maxn],dep[maxn],msz[maxn];
int sum,root,cnt;
int ask[maxn],ans[maxn];
bool vis[maxn];

inline void add(int x,int y,int z){
    to[++e]=y;
    nex[e]=beg[x];
    beg[x]=e;
    w[e]=z;
}
inline void GetRoot(int x,int fa){
    siz[x]=1;msz[x]=0;//siz[x]表示 x 的子樹大小(含自己),msz[x] 表示其子樹中最大的子樹的大小   
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(y==fa||vis[y])continue;
        GetRoot(y,x);
        siz[x]+=siz[y];
        msz[x]=max(msz[x],siz[y]);
    }
    msz[x]=max(msz[x],sum-siz[x]);//按最大子樹最小的標準處理出root   
    if(msz[x]<msz[root])root=x;
}
inline void GR_Init(int x,int fa){
    sum=siz[x];//sum表示當前子樹所有節點個數  
    root=0;//root表示當前子樹根節點  
    GetRoot(x,fa);
}
inline void GetDeep(int x,int fa){//處理深度
    dis[++cnt]=dep[x];
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(vis[y]||y==fa)continue;
        dep[y]=dep[x]+w[i];
        GetDeep(y,x);
    }
}
inline void GD_Init(int x,int val){
    cnt=0;
    dep[x]=val;
    GetDeep(x,0);
}
inline void Calc(int x,int val,int mrk){//計算貢獻
    GD_Init(x,val);
    sort(dis+1,dis+cnt+1);
    for(Rint i=1;i<=m;i++){
        for(Rint l=1,r=cnt;l<r;l++){
            while(l<r&&dis[l]+dis[r]>=ask[i]){
                if(dis[l]+dis[r]==ask[i])ans[i]+=mrk;
                r--;
            }
        }
    }
}
inline void Point_Divide(int x){
    vis[x]=1;
    Calc(x,0,1);//處理出經過這個節點的所有所求貢獻
    for(Rint i=beg[x];i;i=nex[i]){
        int y=to[i];
        if(vis[y])continue;
        Calc(y,w[i],-1);//減去其子樹內不需要被計算但卻被計算的貢獻
        GR_Init(y,x);
        Point_Divide(root);//對其子樹繼續分治
    }
}

int main(){
    //File();
    read(n);read(m);
    for(Rint i=1;i<n;i++){
        int x,y,z;read(x);read(y);read(z);
        add(x,y,z);add(y,x,z);
    }
    for(Rint i=1;i<=m;i++)read(ask[i]);
    msz[0]=inf;sum=n;GetRoot(1,0);
    Point_Divide(root);
    for(Rint i=1;i<=m;i++){
        if(ans[i])printf("AYE\n");
        else printf("NAY\n");
    }
    return 0;
}

模板·點分治(luogu P3806)