BZOJ3697: 採藥人的路徑

【傳送門:BZOJ3697


簡要題意:

  給出一棵n個點的樹,樹上的邊權要麼爲0,要麼爲1
php

  要求找出有多少條路徑,知足:node

  1.路徑上0的數量等於1的數量spa

  2.可以在這條路徑上找到一個點(不包括起點和終點),使得起點到這個點,終點到這個點所構成的兩條路徑都知足條件1code


題解:

  點分治
blog

  對於一個分治中心,咱們處理通過分治中心的路徑數get

  先把邊權爲0的看成邊權爲-1,而後當前分治樹求出每一個點的深度(也就是根到當前點的路徑上的邊權和)string

  若是x到根的路徑上出現與x深度相同的點,則說明這兩個點能夠構成一條知足條件1的路徑,那麼咱們就把x打上標記it

  而後對於每個點進行分類討論:io

  1.深度爲0,沒打標記----->那麼這個點能 和 與本身不在一棵子樹上且深度爲0的點 構成答案路徑ast

  2.深度爲0,打了標記----->那麼這個點能 和 與本身不在一棵子樹上且深度爲0的點 以及 根節點 構成答案路徑

  假設x!=0

  3.深度爲x,沒打標記----->那麼這個點能 和 與本身不在一棵子樹上且深度爲-x且打了標記的點 構成答案路徑

  4.深度爲x,打了標記----->那麼這個點能 和 與本身不在一棵子樹上且深度爲-x的點 構成答案路徑

  上面的操做用tol[0或1][x]記錄每種深度的數量就能作了


參考代碼:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mp(x,y) make_pair(x,y)
#define pin pair<int,int>
#define Maxn 110000
using namespace std;
typedef long long LL;
struct node{int x,y,c,next;}a[Maxn*2];int len,last[Maxn];
void ins(int x,int y,int c){a[++len]=(node){x,y,c,last[x]};last[x]=len;}
int sum,ms[Maxn],tot[Maxn],rt;
bool v[Maxn];
void getrt(int x,int fa)
{
    tot[x]=1;ms[x]=0;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa||v[y]==true) continue;
        getrt(y,x);
        tot[x]+=tot[y];
        ms[x]=max(ms[x],tot[y]);
    }
    ms[x]=max(ms[x],sum-tot[x]);
    if(ms[x]<ms[rt]) rt=x;
}
LL ans;
int dep[Maxn];//深度 
int bo[Maxn];//標記
pin sta[Maxn];int tp;
int cnt[Maxn*2];
int tol1[2][Maxn*2];//點種類的數量,tol[0~1][x+Maxn]表示是否打標記且深度爲x的點數
int tol2[2][Maxn*2];
void getdep(int x,int fa)
{
    if(tol2[bo[x]][dep[x]+Maxn]==1) sta[++tp]=mp(bo[x],dep[x]);
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa||v[y]==true) continue;
        dep[y]=dep[x]+a[k].c;
        if(cnt[dep[y]+Maxn]!=0) bo[y]=1;
        cnt[dep[y]+Maxn]++;
        tol2[bo[y]][dep[y]+Maxn]++;
        getdep(y,x);
        cnt[dep[y]+Maxn]--;
    }
}
int ts[Maxn],sp;
void clear(int x,int fa)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa||v[y]==true) continue;
        tol1[bo[y]][dep[y]+Maxn]--;
        bo[y]=0;
        clear(y,x);
    }
}
void solve(int x,int fa)
{
    v[x]=true;dep[x]=0;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa||v[y]==true) continue;
        dep[y]=a[k].c;tp=0;
        tol2[bo[y]][dep[y]+Maxn]++;
        cnt[dep[y]+Maxn]++;
        getdep(y,0);
        cnt[dep[y]+Maxn]--;
        for(int i=1;i<=tp;i++)
        {
            int p1=sta[i].first,p2=sta[i].second;
            if(p1==0&&p2==0) ans+=(LL)tol2[0][Maxn]*(tol1[0][Maxn]+tol1[1][Maxn]);
            if(p1==1&&p2==0) ans+=(LL)tol2[1][Maxn]*(tol1[0][Maxn]+tol1[1][Maxn]+1);
            if(p1==0&&p2!=0) ans+=(LL)tol2[0][p2+Maxn]*tol1[1][Maxn-p2];
            if(p1==1&&p2!=0) ans+=(LL)tol2[1][p2+Maxn]*(tol1[1][Maxn-p2]+tol1[0][Maxn-p2]);
        }
        for(int i=1;i<=tp;i++)
        {
            int p1=sta[i].first,p2=sta[i].second;
            tol1[p1][p2+Maxn]+=tol2[p1][p2+Maxn];
            tol2[p1][p2+Maxn]=0;
        }
    }
    clear(x,0);
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa||v[y]==true) continue;
        rt=0;sum=tot[y];
        getrt(y,0);
        solve(rt,0);
    }
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("vio.out","w",stdout);
    int n;
    scanf("%d",&n);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        if(c==0) c=-1;
        ins(x,y,c);ins(y,x,c);
    }
    sum=ms[0]=n;
    rt=0;getrt(1,0);
    memset(v,false,sizeof(v));
    tp=0;solve(rt,0);
    printf("%lld\n",ans);
    return 0;
}
相關文章
相關標籤/搜索