BZOJ3697 - 采药人的路径
Description
给出一棵\(n(n\leq10^5)\)个点的树,每条边的边权为\(-1\)或\(1\)。求有多少条无向路径\((u,v)\),\(\exists k\in(u,v)\),满足\((u,k)=0,(k,v)=0\)。
Solution
依然是点分治。
记录\(pre[0][d]\)表示在根及前若干个子树中,到根距离为\(d\)且是首次为\(d\)的点的个数;\(pre[1][d]\)表示非首次距离为\(d\)的点的个数。\(cur[0]\)和\(cur[1]\)相似的记录当前子树的信息。那么\(ans\)增加\(cur[0][d]\times pre[1][-d] + cur[1][d] \times (pre[0][-d]+pre[1][-d])\)。然后将\(cur\)加到\(pre\)中去。注意根是处在\(pre[0][0]\)且在路径上是在所有点之前的,所以\(cur[0][0]\)和\(cur[1][0]\)都应加到\(pre[1][0]\)中。
于是我们就可以统计出过根的满足条件的路径条数,分治下去即可。
时间复杂度\(O(nlogn)\)。
Code
//采药人的路径
#include <algorithm>
#include <cstdio>
using std::max;
typedef long long lint;
inline char gc()
{
static char now[1<<16],*s,*t;
if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
return *s++;
}
inline int read()
{
int x=0; char ch=gc();
while(ch<'0'||'9'<ch) ch=gc();
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x;
}
int const N=1e5+10;
int const ZERO=1e5+10;
int n;
int h[N],cnt;
struct edge{int v,w,nxt;} ed[N<<1];
void edAdd(int u,int v,int w)
{
cnt++; ed[cnt].v=v,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt;
cnt++; ed[cnt].v=u,ed[cnt].w=w,ed[cnt].nxt=h[v],h[v]=cnt;
}
lint ans;
int G,siz0,siz[N],chMax[N]; bool vst[N];
void solve(int u);
void getG(int u,int fa)
{
siz[u]=1,chMax[u]=0;
for(int i=h[u];i;i=ed[i].nxt)
{
int v=ed[i].v;
if(vst[v]||v==fa) continue;
getG(v,u); siz[u]+=siz[v],chMax[u]=max(chMax[u],siz[v]);
}
chMax[u]=max(chMax[u],siz0-siz[u]);
if(chMax[u]<chMax[G]) G=u;
}
int pre[2][N<<1],cur[2][N<<1]; int dCnt[N<<1];
int tCnt,t[N]; bool inT[N<<1];
void getDst(int u,int fa,int d)
{
if(!inT[d]) t[++tCnt]=d,inT[d]=true;
cur[dCnt[d]>0][d]++,dCnt[d]++;
for(int i=h[u];i;i=ed[i].nxt)
{
int v=ed[i].v,w=ed[i].w;
if(vst[v]||v==fa) continue;
getDst(v,u,d+w);
}
dCnt[d]--;
}
lint calc(int u,int d0)
{
tCnt=0; getDst(u,0,ZERO+d0);
lint res=0;
for(int i=1;i<=tCnt;i++)
{
int d=t[i];
res+=(lint)pre[0][ZERO*2-d]*cur[1][d];
res+=(lint)pre[1][ZERO*2-d]*(cur[0][d]+cur[1][d]);
}
for(int i=1;i<=tCnt;i++)
{
int d=t[i]; inT[d]=false;
pre[d==ZERO][d]+=cur[0][d],cur[0][d]=0;
pre[1][d]+=cur[1][d],cur[1][d]=0;
}
return res;
}
void reset(int u,int fa,int d)
{
pre[0][d]=pre[1][d]=0;
for(int i=h[u];i;i=ed[i].nxt)
{
int v=ed[i].v;
if(vst[v]||v==fa) continue;
reset(v,u,d+ed[i].w);
}
}
void DC(int u)
{
vst[u]=true; pre[0][ZERO]=1;
for(int i=h[u];i;i=ed[i].nxt)
{
int v=ed[i].v;
if(vst[v]) continue;
if(siz[v]>siz[u]) siz[v]=siz0-siz[u];
ans+=calc(v,ed[i].w);
}
reset(u,0,ZERO);
for(int i=h[u];i;i=ed[i].nxt) {int v=ed[i].v; if(!vst[v]) solve(v);}
}
void solve(int u) {siz0=siz[u],G=0,getG(u,0),DC(G);}
int main()
{
n=read();
for(int i=1;i<=n-1;i++)
{
int u=read(),v=read(),w=read();
edAdd(u,v,w?1:-1);
}
ans=0;
siz[1]=n,chMax[0]=n,solve(1);
printf("%lld\n",ans);
return 0;
}
P.S.
看题时还有权限,写完就没了...