點分治複習筆記

點分治

學習筆記

總:點分治是處理樹上問題的一個比較好用的工具,時間複雜度是$O(nlogn)$級別的,很是優秀。其實感受很是的暴力,可是它還跑得很快。。。node

點分標準函數:ios

  $find-rt(int\;x,int\;fa)$:用於尋找在$x$所在的子樹中的重心c++

  $work(int\;u)$:$u$表示本棵子樹的重心,也就是說要處理$u$爲重心的這棵子樹的信息,是點分中的主函數。數組

  $dfs(in\;u,int\;fa)$: 這是一個普通的搜索,通常用它來搜出子樹中全部路徑,用於處理路徑信息。app

  $solve(int\;u)$: 用於合併路徑信息,以及統計答案。less

點分能處理的問題:主要是路徑問題吧,還有樹上連通塊問題。ide

點分的基本思路:函數

  就如它的名字同樣,本質上是一個分治。每次都對一個子問題來求解,也就是說$work()$函數每次面對的是一棵子樹的信息,最初的樹就是原樹。工具

  1.找到重心,而後以重心做爲分界點,將目前的樹分爲若干個子樹,繼續分治處理。學習

  2.在每一次遇到一棵樹的時候,都要用$dfs$搜出全部以重心開始的路徑(注意要在本棵子樹內),而後用$solve()$合併。

  注意它和樹形dp的區別在於它幾乎不作上傳,也就是說,它每次處理問題都是獨立的。

例題

P3806 【模板】點分治1

題目大意:

給定一棵有n個點的樹

詢問樹上距離爲k的點對是否存在。

題解:

點分模板題,點分能處理的標準問題。就是要把點分的函數都一個一個實現一下。

 

首先是尋找重心

 1 void find_rt(int u,int fa){
 2     siz[u]=1;
 3     int t,vv;
 4     t=indexx[u];
 5     while(t){
 6         vv=edge[t].v;
 7         if(vv!=fa && !vist[vv]){
 8             find_rt(vv,u);
 9             siz[u]+=siz[vv];
10             f[u]=max(f[u],siz[vv]);
11         }
12         t=edge[t].nxt;
13     }
14     f[u]=max(f[u],sum-siz[u]);
15     if(f[u]<f[rt]) rt=u;
16 }

注意$f[0]$要賦成$INF$。

具體來講就是找出當前子樹裏一個點使它的最重子樹儘量輕,$f[u]$ 表示$u$最重子樹的重量,那個$vist$數組是防止它搜出當前樹的。

 

而後是點分主函數$work()$

 1 void work(int u){
 2     vist[u]=1;
 3     solve(u,0,1);
 4     int t,vv;
 5     t=indexx[u];
 6     while(t){
 7         vv=edge[t].v;
 8         if(!vist[vv]){
 9             solve(vv,edge[t].q*2,-1);
10             sum=siz[vv];
11             rt=0;
12             find_rt(vv,u);
13             work(rt);
14         }
15         t=edge[t].nxt;
16     }
17 }

這個就是分治的主函數了。每次進入該樹以後進行$solve()$第二個$solve()$是爲了去重,由於可能兩條路徑有重疊部分,因此須要把重疊部分減掉。而後在子樹中繼續分治,先找重心,而後繼續遞歸。

 

而後是處理部分$solve()$

 1 void solve(int u,int alr,int type){
 2     tot=0;
 3     dfs(u,0,0);
 4     sort(dist+1,dist+tot+1);
 5     int l=1,r=tot;
 6     for(int i=1;i<=m;i++){
 7         l=1,r=tot;
 8         while(l<r){
 9             if(dist[l]+dist[r]+alr<Q[i]) l++;
10             else if(dist[l]+dist[r]+alr==Q[i]){
11                 int tl=l;
12                 for(;dist[l]==dist[tl]&&tl<r;tl++) al[i]+=0;
13                 int tr=r;
14                 for(;dist[r]==dist[tr]&&tr>=tl;tr--) al[i]+=(tl-l)*type;
15                 r=tr;l=tl;
16             }
17             else r--;
18         }
19     }
20 }

先用$dfs()$搜出本棵子樹裏以$u$爲起點的全部路徑,而後用雙指針合併。那個$alr$是爲了去重使用的,中間詭異的$tl$和$tr$是由於可能會有長度同樣的。

 

而後就是$dfs()$了

 1 void dfs(int u,int fa,int dep){
 2     int t,vv,qq;
 3     t=indexx[u];
 4     dist[++tot]=dep;
 5     while(t){
 6         vv=edge[t].v;
 7         qq=edge[t].q;
 8         if(!vist[vv] && vv!=fa){
 9             dfs(vv,u,dep+qq);
10         }
11         t=edge[t].nxt;
12     }
13 }

這個不解釋應該也能看懂吧,就是搜出全部路徑長度存在$dist[]$裏。

那麼這道題就作完了,點分的套路大概就是這樣。這道題是用的容斥去重,也能夠分不一樣的兒子進行討論那樣的,實現起來也差很少,但我感受分兒子討論的那個更直觀一些,這個更好寫一些。

最後貼代碼。(個人鏈式前向星寫的比較奇怪,你們就理解一下$vv$是它的兒子就好了)($define\;int\;long\;long$大法好啊)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define N 20011
#define int long long
using namespace std;
int siz[N],sum,al[N],dist[N],vist[N],indexx[N],rt,f[N],tot,n,m,Q[N],cnt;
struct apple{
    int v,nxt,q;
}edge[N*4];
void addedge(int x ,int y,int z){
    edge[++cnt].v=y;
    edge[cnt].q=z;
    edge[cnt].nxt=indexx[x];
    indexx[x]=cnt;
} 
void find_rt(int u,int fa){
    siz[u]=1;
    int t,vv;
    t=indexx[u];
    while(t){
        vv=edge[t].v;
        if(vv!=fa && !vist[vv]){
            find_rt(vv,u);
            siz[u]+=siz[vv];
            f[u]=max(f[u],siz[vv]);
        }
        t=edge[t].nxt;
    }
    f[u]=max(f[u],sum-siz[u]);
    if(f[u]<f[rt]) rt=u;
}
void dfs(int u,int fa,int dep){
    int t,vv,qq;
    t=indexx[u];
    dist[++tot]=dep;
    while(t){
        vv=edge[t].v;
        qq=edge[t].q;
        if(!vist[vv] && vv!=fa){
            dfs(vv,u,dep+qq);
        }
        t=edge[t].nxt;
    }
}
void solve(int u,int alr,int type){
    tot=0;
    dfs(u,0,0);
    sort(dist+1,dist+tot+1);
    int l=1,r=tot;
    for(int i=1;i<=m;i++){
        l=1,r=tot;
        while(l<r){
            if(dist[l]+dist[r]+alr<Q[i]) l++;
            else if(dist[l]+dist[r]+alr==Q[i]){
                int tl=l;
                for(;dist[l]==dist[tl]&&tl<r;tl++) al[i]+=0;
                int tr=r;
                for(;dist[r]==dist[tr]&&tr>=tl;tr--) al[i]+=(tl-l)*type;
                r=tr;l=tl;
            }
            else r--;
        }
    }
}
void work(int u){
    vist[u]=1;
    solve(u,0,1);
    int t,vv;
    t=indexx[u];
    while(t){
        vv=edge[t].v;
        if(!vist[vv]){
            solve(vv,edge[t].q*2,-1);
            sum=siz[vv];
            rt=0;
            find_rt(vv,u);
            work(rt);
        }
        t=edge[t].nxt;
    }
}
signed main(){
//    freopen("testdata.in","r",stdin);
    int x,y,z;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<n;i++){
        scanf("%lld%lld%lld",&x,&y,&z);
        addedge(x,y,z);
        addedge(y,x,z);
    }
    for(int i=1;i<=m;i++){
        scanf("%lld",&Q[i]);
    }
//    sort(Q+1,Q+m+1,less<int>());
    f[0]=200000000;
    sum=n;
    rt=0;
    find_rt(1,0);
    work(rt);
    for(int i=1;i<=m;i++){
        if(al[i]) printf("AYE\n");
        else printf("NAY\n");
    }
    return 0;
}
View Code

 

BZOJ2599 [IOI2011]Race

題意:

給一棵樹,每條邊有權。求一條簡單路徑,權值和等於$K$,且邊的數量最小。

題解:

依舊是點分標準操做。

可是發現它求的不是總數,而是最值,因此用去重法可能會比較麻煩吧。因爲$K$的值用數組開得下,在這裏用的是分不一樣兒子的方法。

每次在$dfs$的時候就順便統計了答案,在遍歷完重心的一個兒子子樹後再把它的信息推入到$hashh$數組中,這樣在更新的時候用的$hashh$值表明的鏈必定和本條鏈沒有重疊部分,這樣就能夠直接統計答案了。

每次注意清$hashh$,注意不要$memset$。

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <cstdio>
  5 #include <stack>
  6 #define N 200011
  7 #define INF 0x7f7f7f7f
  8 using namespace std;
  9 struct apple{
 10     int v,nxt,q;
 11 }edge[N*2];
 12 int indexx[N],siz[N],f[N],deep[N],dist[N],hashh[N*10],n,k,ans=INF,tot,vist[N],sum,rt;
 13 stack<int> pq,Q;
 14 void addedge(int x,int y,int z){
 15     edge[++tot].v=y;
 16     edge[tot].nxt=indexx[x];
 17     edge[tot].q=z;
 18     indexx[x]=tot;
 19 }
 20 void find_root(int x,int fa){
 21     siz[x]=1;
 22     f[x]=0;
 23     int t,vv;
 24     t=indexx[x];
 25     while(t){
 26         vv=edge[t].v;
 27         if(!vist[vv] && vv!=fa){
 28             find_root(vv,x);
 29             siz[x]+=siz[vv];
 30             f[x]=max(f[x],siz[vv]);
 31         }
 32         t=edge[t].nxt;
 33     }
 34     f[x]=max(f[x],sum-siz[x]);
 35     if(f[x]<f[rt]) rt=x;
 36 }
 37 void dfs(int u,int fa,int ancestor){
 38     if(k>=dist[u]){
 39         ans=min(ans,deep[u]+hashh[k-dist[u]]);
 40     }
 41     int t=indexx[u],vv,x;
 42     while(t){
 43         vv=edge[t].v;
 44         if(!vist[vv] && vv!=fa){
 45             dist[vv]=dist[u]+edge[t].q;
 46             deep[vv]=deep[u]+1;
 47             pq.push(vv);
 48             dfs(vv,u,ancestor);
 49         }
 50         t=edge[t].nxt;
 51     }
 52     if(fa==ancestor){
 53         x=pq.top();
 54         pq.pop();
 55         if(dist[x]<=k){
 56             hashh[dist[x]]=min(hashh[dist[x]],deep[x]);
 57             Q.push(dist[x]);
 58         }
 59         while(x!=u){
 60             x=pq.top();
 61             pq.pop();
 62             if(dist[x]<=k){
 63                 hashh[dist[x]]=min(hashh[dist[x]],deep[x]);
 64                 Q.push(dist[x]);
 65             }
 66         }
 67     }
 68 }
 69 void Clear(){
 70     int x;
 71     while(!Q.empty()){
 72         x=Q.top();
 73         Q.pop();
 74         hashh[x]=INF;
 75     }
 76 }
 77 void work(int now){
 78     dist[now]=0;
 79     deep[now]=0;
 80     vist[now]=1;
 81     dfs(now,0,now);
 82     ans=min(ans,hashh[k]);
 83     Clear();
 84     int t=indexx[now],vv;
 85     while(t){
 86         vv=edge[t].v;
 87         if(!vist[vv]){
 88             rt=0;
 89             sum=siz[vv];
 90             find_root(vv,0);
 91             work(rt);
 92         }
 93         t=edge[t].nxt;
 94     }
 95 }
 96 int main(){
 97     int x,y,z;
 98     scanf("%d%d",&n,&k);
 99     sum=n;
100     for(int i=1;i<n;i++){
101         scanf("%d%d%d",&x,&y,&z);
102         x++;y++;
103         addedge(x,y,z);
104         addedge(y,x,z);
105     }
106     memset(hashh,0x7f,sizeof(hashh));
107     f[0]=INF;
108     rt=0;
109     find_root(1,0);
110     work(rt);
111     if(ans==INF) printf("-1");
112     else printf("%d",ans);
113     return 0;
114 }
View Code

P2634 [國家集訓隊]聰聰可可

題意:

給出一棵樹,邊有邊權,求兩人分別獨立任選一個點後兩點之間簡單路徑邊權和是3的倍數的機率。

題解:

點分,又是點分。直接水過~。把$dp[i]$與$dp[3-i-alr]$乘起來就好了,因爲兩我的是獨立操做的,因此可能選到同一個點的。這麼寫再加去重寫法就能夠了。分母是$n*n$,約分便可。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#define N 20011
using namespace std;
int siz[N],n,indexx[N],f[N],tot,hashh[4],ans,vist[N],sum,Stack[N],top,rt;
struct apple{
    int v,nxt,q;
}edge[N*4];
int gcd(int a,int b){
    if(b==0) return a;
    return gcd(b,a%b);
}
void addedge(int x,int y,int z){
    edge[++tot].v=y;
    edge[tot].q=z;
    edge[tot].nxt=indexx[x];
    indexx[x]=tot;
}
void find_rt(int u,int fa){
    int t,vv;
    siz[u]=1;
    f[u]=0;
    t=indexx[u];
    while(t){
        vv=edge[t].v;
        if(!vist[vv] && vv!=fa){
            find_rt(vv,u);
            siz[u]+=siz[vv];
            f[u]=max(f[u],siz[vv]);
        }
        t=edge[t].nxt;
    }
    f[u]=max(f[u],sum-siz[u]);
    if(f[u]<f[rt]) rt=u;
}
void dfs(int u,int fa,int dep){
    Stack[++top]=dep;
    int t,vv;
    t=indexx[u];
    while(t){
        vv=edge[t].v;
        if(!vist[vv] && vv!=fa){
            dfs(vv,u,dep+edge[t].q);
        }
        t=edge[t].nxt;
    }
}
int solve(int u,int alr){
    int ret=0;
    alr%=3;
    top=0;
    dfs(u,0,0);
    hashh[0]=hashh[1]=hashh[2]=0;
    for(int i=1;i<=top;i++){
        hashh[Stack[i]%3]++;
    }
    for(int i=0;i<=2;i++){
        ret+=hashh[i]*hashh[(30-i-alr)%3];
    }
    return ret;
}
void work(int u){
    ans+=solve(u,0);
    vist[u]=1;
    int t,qq,vv;
    t=indexx[u];
    while(t){
        vv=edge[t].v;
        qq=edge[t].q;
        if(!vist[vv]){
            ans-=solve(vv,qq*2);
            rt=0;
            sum=siz[vv];
            find_rt(vv,u);
            work(rt);
        }
        t=edge[t].nxt;
    }
}
int main(){
    int x,y,z;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);
        addedge(y,x,z);
    }
    sum=n;
    rt=0;
    f[0]=200000000;
    find_rt(1,0);
    work(rt);
    int p=n*n;
    int tmp=gcd(p,ans);
    p/=tmp;
    ans/=tmp;
    printf("%d/%d",ans,p);
    return 0;
}
聰聰可可

P2993 [FJOI2014]最短路徑樹問題

題意:

給一個無向連通圖,要求求一個字典序最小的最短路樹,而後求最長的包含K個點的簡單路徑長度、長度爲該最長長度的不一樣路徑條數。

題解:

和Race那題比較像吧,同樣的方法水過~

  1 #include <algorithm>
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <vector>
  7 #include <queue>
  8 #include <stack>
  9 #define X 0
 10 #define Y 1
 11 #define Z 2
 12 #define N 60011
 13 using namespace std;
 14 int f[N],siz[N],dist[N],ee[N][3],n,m,used[N],hashh[N],rt,vist[N],indexx[N],tot,sum,k,deep[N],ans;
 15 int cnt,has[N];
 16 struct node{
 17     int y,z,no;
 18     bool operator<(const node &other)const{
 19         return y<other.y;
 20     }
 21     node(int yy,int zz,int nn){
 22         y=yy;
 23         z=zz;
 24         no=nn;
 25     }
 26     node(){}
 27 };
 28 struct apple{
 29     int no,dist,pre;
 30     bool operator<(const apple &other)const{
 31         return dist>other.dist;
 32     }
 33 }temp;
 34 struct point{
 35     int v,nxt,q;
 36 }edge[N*2];
 37 priority_queue<apple> pq;
 38 vector<node> Map[N];
 39 stack<int> pp,Q;
 40 void addedge(int x,int y,int z){
 41     edge[++tot].v=y;
 42     edge[tot].q=z;
 43     edge[tot].nxt=indexx[x];
 44     indexx[x]=tot;
 45 }
 46 void dij(){
 47     int x,vv,qq;
 48     x=1;
 49     temp.no=1;
 50     dist[1]=0;
 51     temp.dist=0;
 52     pq.push(temp);
 53     while(!pq.empty()){
 54         temp=pq.top();
 55         pq.pop();
 56         x=temp.no;
 57         if(vist[x]) continue;
 58         used[temp.pre]=1;
 59         vist[x]=1;
 60         for(int i=0;i<Map[x].size();i++){
 61             vv=Map[x][i].y;
 62             qq=Map[x][i].z;
 63             if(dist[vv]>dist[x]+qq){
 64                 dist[vv]=dist[x]+qq;
 65                 temp.no=vv;
 66                 temp.dist=dist[vv];
 67                 temp.pre=Map[x][i].no;
 68                 pq.push(temp);
 69             }
 70         }
 71     }
 72 }
 73 void find_root(int x,int fa){
 74     siz[x]=1;
 75     f[x]=0;
 76     int t=indexx[x],vv;
 77     while(t){
 78         vv=edge[t].v;
 79         if(!vist[vv] && vv!=fa){
 80             find_root(vv,x);
 81             siz[x]+=siz[vv];
 82             f[x]=max(f[x],siz[vv]);
 83         }
 84         t=edge[t].nxt;
 85     }
 86     f[x]=max(f[x],sum-siz[x]);
 87     if(f[x]<f[rt]) rt=x;
 88 }
 89 void dfs(int u,int fa,int ancestor){
 90     int ta;
 91     if(k>=deep[u] && hashh[k-deep[u]+1]){
 92         if(hashh[k-deep[u]+1]+dist[u]==ans) cnt+=has[k-deep[u]+1];
 93         else if(hashh[k-deep[u]+1]+dist[u]>ans){
 94             ans=hashh[k-deep[u]+1]+dist[u];
 95             cnt=has[k-deep[u]+1];
 96         }
 97     }
 98     int t=indexx[u],vv,qq,x;
 99     while(t){
100         vv=edge[t].v;
101         qq=edge[t].q;
102         if(!vist[vv] && vv!=fa){
103             dist[vv]=dist[u]+qq;
104             deep[vv]=deep[u]+1;
105             pp.push(vv);
106             dfs(vv,u,ancestor);
107         }
108         t=edge[t].nxt;
109     }
110     if(fa==ancestor){
111         x=pp.top();
112         pp.pop();
113         if(deep[x]<=k){
114             if(dist[x]==hashh[deep[x]]) has[deep[x]]++;
115             else if(dist[x]>hashh[deep[x]]){
116                 hashh[deep[x]]=dist[x];
117                 has[deep[x]]=1;
118             }
119             Q.push(deep[x]);
120         }
121         while(x!=u){
122             x=pp.top();
123             pp.pop();
124             if(deep[x]<=k){
125                 if(dist[x]==hashh[deep[x]]) has[deep[x]]++;
126                 else if(dist[x]>hashh[deep[x]]){
127                     hashh[deep[x]]=dist[x];
128                     has[deep[x]]=1;
129                 }
130                 Q.push(deep[x]);
131             }
132         }
133     }
134 }
135 void Clear(){
136     int x;
137     while(!Q.empty()){
138         x=Q.top();
139         Q.pop();
140         hashh[x]=0;
141         has[x]=0;
142     }
143 }
144 void work(int now){
145     dist[now]=0;
146     deep[now]=1;
147     vist[now]=1;
148     dfs(now,0,now);
149     if(ans==hashh[k]) cnt+=has[k];
150     else if(ans<hashh[k]){
151         ans=hashh[k];
152         cnt=has[k];
153     }
154     Clear();
155     int t=indexx[now],vv;
156     while(t){
157         vv=edge[t].v;
158         if(!vist[vv]){
159             rt=0;
160             sum=siz[vv];
161             find_root(vv,0);
162             work(rt);
163         }
164         t=edge[t].nxt;
165     }
166 }
167 int main(){
168     int x,y,z;
169     scanf("%d%d%d",&n,&m,&k);
170     for(int i=1;i<=m;i++){
171         scanf("%d%d%d",&x,&y,&z);
172         ee[i][X]=x;
173         ee[i][Y]=y;
174         ee[i][Z]=z;
175         Map[x].push_back(node(y,z,i));
176         Map[y].push_back(node(x,z,i));
177     }
178     for(int i=1;i<=n;i++){
179         sort(Map[i].begin(),Map[i].end());
180     }
181     memset(dist,127,sizeof(dist));
182     dij();
183     memset(vist,0,sizeof(vist));
184     memset(dist,0,sizeof(dist));
185     for(int i=1;i<=m;i++){
186         if(used[i]){
187             addedge(ee[i][X],ee[i][Y],ee[i][Z]);
188             addedge(ee[i][Y],ee[i][X],ee[i][Z]);
189         }
190     }
191     sum=n;
192     rt=0;
193     f[0]=2000000000;
194     find_root(1,0);
195     work(rt);
196     printf("%d %d",ans,cnt);
197     return 0;
198 }
最短路徑樹

P3727 曼哈頓計劃E

題意:

給出一棵樹,點有點權,給出一種博弈方式,問存不存在一條鏈,使在該鏈上博弈時,先手必敗。

題解:

博弈上樹。首先你須要會一些基本博弈,並打表發現它們的規律。寫出一個$Get\-SG$函數。(打表真費勁)

 1 int get_SG(int x,int op){
 2     if(op==1) return x;
 3     else if(op==2){
 4         if(s%2==1) return x%2;
 5         else{
 6             int len=s+1;
 7             x%=len;if(x==0) x=len;
 8             if(x<=len-2) return x%2;
 9             else if(x==len-1) return 2;
10             else return 0;
11         }
12     }
13     else if(op==3){
14         return x/s;
15     }
16     else if(op==4){
17         if(x==0) return 0;
18         else if(x%4==1 || x%4==2) return x;
19         else if(x%4==3) return x+1;
20         else return x-1;
21     }
22 }

而後就能夠重賦點權爲該點的$SG$值了,這樣問題就轉變爲了是否存在一條鏈,使得點權異或和爲0。發現點權太大數組存不下,因此考慮掛鏈hash。而後就結束了

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <cstdio>
  5 #define N 60011
  6 #define MAX 1313131
  7 #define int long long
  8 using namespace std;
  9 int val[N],dp[N],n,tot,indexx[N],s,siz[N],f[N],rt,sum,vist[N],dist[N],cnt,ans,ind[MAX*2],tt;
 10 struct apple{
 11     int v,nxt,cnt;
 12 }edge[N*4],ee[N*4];
 13 inline int read(){
 14     int ret=0,f=1;char ch=getchar();
 15     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 16     while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
 17     return ret;
 18 }
 19 void add_to_hash(int x){
 20     ee[++tt].v=x;
 21     ee[tt].nxt=ind[x%MAX];
 22     ee[tt].cnt=1;
 23     ind[x%MAX]=tt;
 24 }
 25 int find_hash(int x){
 26     int t=ind[x%MAX],vv,ret=0;
 27     while(t){
 28         vv=ee[t].v;
 29         if(vv==x){
 30             return ee[t].cnt;
 31         }
 32         t=ee[t].nxt;
 33     }
 34     return 0;
 35 }
 36 void addedge(int x,int y){
 37     edge[++tot].v=y;
 38     edge[tot].nxt=indexx[x];
 39     indexx[x]=tot;
 40 }
 41 int get_SG(int x,int op){
 42     if(op==1) return x;
 43     else if(op==2){
 44         if(s%2==1) return x%2;
 45         else{
 46             int len=s+1;
 47             x%=len;if(x==0) x=len;
 48             if(x<=len-2) return x%2;
 49             else if(x==len-1) return 2;
 50             else return 0;
 51         }
 52     }
 53     else if(op==3){
 54         return x/s;
 55     }
 56     else if(op==4){
 57         if(x==0) return 0;
 58         else if(x%4==1 || x%4==2) return x;
 59         else if(x%4==3) return x+1;
 60         else return x-1;
 61     }
 62 }
 63 void find_rt(int u,int fa){
 64     siz[u]=1;
 65     int t,vv;
 66     f[u]=0;
 67     t=indexx[u];
 68     while(t){
 69         vv=edge[t].v;
 70         if(!vist[vv] && vv!=fa){
 71             find_rt(vv,u);
 72             siz[u]+=siz[vv];
 73             f[u]=max(f[u],siz[vv]);
 74         }
 75         t=edge[t].nxt;
 76     }
 77     f[u]=max(f[u],sum-siz[u]);
 78     if(f[u]<f[rt]) rt=u;
 79 }
 80 void dfs(int u,int fa,int dep){
 81     dist[++cnt]=dep;
 82     int t,vv;
 83     t=indexx[u];
 84     while(t){
 85         vv=edge[t].v;
 86         if(vv!=fa && vist[vv]==0){
 87             dfs(vv,u,dep^dp[vv]);
 88         }
 89         t=edge[t].nxt;
 90     }
 91 }
 92 inline int solve(int u,int x){
 93     int ret=0;
 94     cnt=0;
 95     dfs(u,0,0);
 96     tt=0;
 97     for(int i=2;i<=cnt;i++){
 98         if(find_hash(dist[i]^x)){
 99             int t,vv;
100             t=ind[(dist[i]^x)%MAX];
101             while(t){
102                 vv=ee[t].v;
103                 if(vv==(dist[i]^x)){
104                     edge[t].cnt++;
105                     break;
106                 }
107                 t=ee[t].nxt;
108             }
109         }
110         else add_to_hash(dist[i]^x);
111     }
112     for(int i=2;i<=cnt;i++){
113         ret+=find_hash(dist[i]);
114     }tot=0;
115     for(int i=2;i<=cnt;i++){
116         ind[(dist[i]^x)%MAX]=0;
117     }
118     return ret;
119 }
120 void work(int u){
121     vist[u]=1;
122     ans+=solve(u,dp[u]);
123     int t,vv;
124     t=indexx[u];
125     while(t){
126         vv=edge[t].v;
127         if(!vist[vv]){
128             ans-=solve(vv,dp[u]);
129             sum=siz[vv];
130             rt=0;
131             find_rt(vv,u);
132             work(rt);
133         }
134         t=edge[t].nxt;
135     }
136 }
137 signed main(){
138     int op,T,x,y;
139     T=read();
140     while(T--){
141         memset(indexx,0,sizeof(indexx));tot=0;
142         ans=0;
143         memset(vist,0,sizeof(vist));
144         n=read();
145         for(int i=1;i<n;i++){
146             x=read();y=read();
147             addedge(x,y);
148             addedge(y,x);
149         }
150         for(int i=1;i<=n;i++){
151             val[i]=read();
152         }
153         op=read();
154         if(op==2 || op==3) s=read();
155         for(int i=1;i<=n;i++){
156             dp[i]=get_SG(val[i],op);
157         }
158         f[0]=0x7ffffffffffff;
159         rt=0;
160         sum=n;
161         find_rt(1,0);
162         work(rt);
163         if(ans) printf("Mutalisk ride face how to lose?\n");
164         else printf("The commentary cannot go on!\n");
165     }
166     return 0;
167 }
曼哈頓計劃E

P4886 快遞員

題意:

給出一棵樹,有邊權。給出若干個點對$(x,y)$,要求找出一個點$u$,使得對於全部給出點對$dist(x,u)+dist(y,u)$的最大值最小,輸出那個最大值的最小值。

題解:

終於不是套路題了。這道題還有點意思,首先仍是要點分,算出u爲該點時全部點對的答案(由兩條鏈拼起來)。而後考慮一下在什麼狀況下答案不能夠更優:

1.最大值由出現於兩顆不一樣子樹的鏈拼起來獲得。這樣若是咱們移動$u$的話,不論是向$x$方向仍是向$y$方向,都會致使一條鏈變長,一條鏈變短,總長不變。若是向其餘方向移動的話必定更不優。

2.最大值有多組,且它們不位於同一棵子樹內。同理也是,無論向哪一個方向移動都會使至少一個鏈組增加,變得更大。

這兩種狀況能夠直接輸出答案。除了這兩種狀況的話就還剩下每組最大值的兩條鏈都出現同一棵子樹裏,那麼咱們只要把$u$移向那個子樹方向中的重心就好了。

感受仍是有點意思,比花式模板要強一些。注意若是有一個端點在$u$點的狀況,也算到不一樣子樹裏。

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <vector>
  5 #define N 100011
  6 #define INF 0x7f7f7f7f
  7 using namespace std;
  8 struct apple{
  9     int v,nxt,q;
 10 }edge[N*4];
 11 int indexx[N],vist[N],dist[N],col[N],n,m,Q[N][2],tot,f[N],sum,siz[N],rt,used[N],ans=INF;
 12 void addedge(int x,int y,int z){
 13     edge[++tot].v=y;
 14     edge[tot].q=z;
 15     edge[tot].nxt=indexx[x];
 16     indexx[x]=tot;
 17 }
 18 void find_rt(int u,int fa){
 19     int t,vv;
 20     t=indexx[u];
 21     f[u]=0;siz[u]=1;
 22     while(t){
 23         vv=edge[t].v;
 24         if(!vist[vv] && vv!=fa){
 25             find_rt(vv,u);
 26             siz[u]+=siz[vv];
 27             f[u]=max(f[u],siz[vv]);
 28         }
 29         t=edge[t].nxt;
 30     }
 31     f[u]=max(f[u],sum-siz[u]);
 32     if(f[u]<f[rt]) rt=u;
 33 }
 34 void dfs(int u,int fa,int c){
 35     if(fa==0) c=0; 
 36     int t,vv;
 37     col[u]=c;
 38     t=indexx[u];
 39     while(t){
 40         vv=edge[t].v;
 41         if(vv!=fa){
 42             dist[vv]=dist[u]+edge[t].q;
 43             if(fa==0) c++;
 44             dfs(vv,u,c);
 45         }
 46         t=edge[t].nxt;
 47     }
 48 }
 49 int solve(int u,int &maxx){
 50     int cnt=0,k=0;
 51     maxx=0;
 52     dist[u]=0;
 53     dfs(u,0,0);
 54     for(int i=1;i<=m;i++){
 55         int x=Q[i][0];
 56         int y=Q[i][1];
 57         used[i]=0;
 58         if(dist[x]+dist[y]>maxx){
 59             maxx=dist[x]+dist[y];
 60             used[i]=++cnt;
 61         }
 62         else if(dist[x]+dist[y]==maxx){
 63             used[i]=cnt;
 64         }
 65     }
 66     for(int i=1;i<=m;i++){
 67         int x=Q[i][0];
 68         int y=Q[i][1];
 69         if(used[i]==cnt){
 70             if(col[x]!=col[y]) return -1;
 71             else if(k && col[x]!=k) return -1;
 72             k=col[x];
 73         }
 74     }
 75     return k;
 76 }
 77 void work(int u){
 78     vist[u]=1;
 79     int maxx=0,t=indexx[u],vv;
 80     int k=solve(u,maxx);
 81     ans=min(ans,maxx);
 82     while(t){
 83         vv=edge[t].v;
 84         if(col[vv]==k && !vist[vv]){
 85             rt=0;sum=siz[vv];
 86             find_rt(vv,u);
 87             work(rt);
 88         }
 89         t=edge[t].nxt;
 90     }
 91 }
 92 int main(){
 93     int x,y,z;
 94     scanf("%d%d",&n,&m);
 95     for(int i=1;i<n;i++){
 96         scanf("%d%d%d",&x,&y,&z);
 97         addedge(x,y,z);
 98         addedge(y,x,z);
 99     }
100     for(int i=1;i<=m;i++){
101         scanf("%d%d",&x,&y);
102         Q[i][0]=x;
103         Q[i][1]=y;
104     }
105     rt=0;f[0]=INF;
106     sum=n;
107     find_rt(1,0);
108     work(rt);
109     printf("%d",ans);
110     return 0;
111 }
快遞員

P3714 [BJOI2017]樹的難題

題意:

給你一棵 n 個點的無根樹。

樹上的每條邊具備顏色。一共有 m 種顏色,編號爲 1 到 m。第 i 種顏色的權值爲 ci。

對於一條樹上的簡單路徑,路徑上通過的全部邊按順序組成一個顏色序列,序列能夠劃分紅若干個相同顏色段。

定義路徑權值爲顏色序列上每一個同顏色段的顏色權值之和。

計算通過邊數在 l 到 r 之間的全部簡單路徑中,路徑權值的最大值。

題解:

這道題也沒那麼模板。挺有意思的,加了一個顏色限制,加了一個長度限制。

能夠有感受,這個在處理的時候確定不能用去重寫法,因此咱們考慮分開兒子討論。

咱們假設兒子的顏色爲直接連向兒子的邊的顏色,那麼對於兩條樹鏈合併時,就要看兒子的顏色是否同樣,同樣的話就要減去一個貢獻。

那也不能暴力枚舉啊,因此咱們把兒子按顏色排序,暫時記錄一下相同顏色兒子的信息。這樣發現當前兒子顏色和上一個兒子顏色不一樣時就把暫時記錄的信息和另外一組合並。

而後由於還有長度要求啊,是一段區間,因此咱們考慮線段樹。要區間求最大值。

那麼咱們開兩棵線段樹,一棵用來記錄顏色相同,一棵用來記錄顏色不一樣的,而後能夠啓發式一下,按顏色爲第一關鍵字,重量爲第二關鍵字排個序,再線段樹合併就好了。

感受想到點分以後就沒那麼難了呢。(徹底忘了當時的困難)

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <vector>
  5 #define N 100011
  6 #define INF 0x7f7f7f7f
  7 using namespace std;
  8 struct apple{
  9     int v,nxt,q;
 10 }edge[N*4];
 11 int indexx[N],vist[N],dist[N],col[N],n,m,Q[N][2],tot,f[N],sum,siz[N],rt,used[N],ans=INF;
 12 void addedge(int x,int y,int z){
 13     edge[++tot].v=y;
 14     edge[tot].q=z;
 15     edge[tot].nxt=indexx[x];
 16     indexx[x]=tot;
 17 }
 18 void find_rt(int u,int fa){
 19     int t,vv;
 20     t=indexx[u];
 21     f[u]=0;siz[u]=1;
 22     while(t){
 23         vv=edge[t].v;
 24         if(!vist[vv] && vv!=fa){
 25             find_rt(vv,u);
 26             siz[u]+=siz[vv];
 27             f[u]=max(f[u],siz[vv]);
 28         }
 29         t=edge[t].nxt;
 30     }
 31     f[u]=max(f[u],sum-siz[u]);
 32     if(f[u]<f[rt]) rt=u;
 33 }
 34 void dfs(int u,int fa,int c){
 35     if(fa==0) c=0; 
 36     int t,vv;
 37     col[u]=c;
 38     t=indexx[u];
 39     while(t){
 40         vv=edge[t].v;
 41         if(vv!=fa){
 42             dist[vv]=dist[u]+edge[t].q;
 43             if(fa==0) c++;
 44             dfs(vv,u,c);
 45         }
 46         t=edge[t].nxt;
 47     }
 48 }
 49 int solve(int u,int &maxx){
 50     int cnt=0,k=0;
 51     maxx=0;
 52     dist[u]=0;
 53     dfs(u,0,0);
 54     for(int i=1;i<=m;i++){
 55         int x=Q[i][0];
 56         int y=Q[i][1];
 57         used[i]=0;
 58         if(dist[x]+dist[y]>maxx){
 59             maxx=dist[x]+dist[y];
 60             used[i]=++cnt;
 61         }
 62         else if(dist[x]+dist[y]==maxx){
 63             used[i]=cnt;
 64         }
 65     }
 66     for(int i=1;i<=m;i++){
 67         int x=Q[i][0];
 68         int y=Q[i][1];
 69         if(used[i]==cnt){
 70             if(col[x]!=col[y]) return -1;
 71             else if(k && col[x]!=k) return -1;
 72             k=col[x];
 73         }
 74     }
 75     return k;
 76 }
 77 void work(int u){
 78     vist[u]=1;
 79     int maxx=0,t=indexx[u],vv;
 80     int k=solve(u,maxx);
 81     ans=min(ans,maxx);
 82     while(t){
 83         vv=edge[t].v;
 84         if(col[vv]==k && !vist[vv]){
 85             rt=0;sum=siz[vv];
 86             find_rt(vv,u);
 87             work(rt);
 88         }
 89         t=edge[t].nxt;
 90     }
 91 }
 92 int main(){
 93     int x,y,z;
 94     scanf("%d%d",&n,&m);
 95     for(int i=1;i<n;i++){
 96         scanf("%d%d%d",&x,&y,&z);
 97         addedge(x,y,z);
 98         addedge(y,x,z);
 99     }
100     for(int i=1;i<=m;i++){
101         scanf("%d%d",&x,&y);
102         Q[i][0]=x;
103         Q[i][1]=y;
104     }
105     rt=0;f[0]=INF;
106     sum=n;
107     find_rt(1,0);
108     work(rt);
109     printf("%d",ans);
110     return 0;
111 }
樹的難題

總結

點分在處理樹鏈的問題仍是很強的,可是也不要爲了寫點分而忘記了樹形$dp$。。。不要思惟僵化

相關文章
相關標籤/搜索