bzoj千题计划248:bzoj3697: 采药人的路径
http://www.lydsy.com/JudgeOnline/problem.php?id=3697
点分治
路径0改为路径-1
g[i][0/1] 和 f[i][0/1]分别表示当前子树 和 已经处理完的兄弟节点子树 中,路径前缀和为i,前面是否还有一个前缀和为i的点
合法的路径分为三类:
1、路径以当前分治重心为端点,ans+=g[0][1]
2、路径跨越分治重心,休息站在分治重心,ans+=g[0][0]*f[0][0]
3、路径跨越分治重心,休息站不在分治重心,ans+= Σ g[i][0]*f[-i][1] + g[i][1]*f[-i][0] + g[i][1]*f[-i][1] i∈[min_dis,max_dis]
#include<cstdio> #include<iostream> #define N 100001 using namespace std; typedef long long LL; int n; int tot,front[N],to[N<<1],nxt[N<<1],val[N<<1]; int root,min_size; int siz[N],mx[N]; bool vis[N]; LL g[N<<1][2],f[N<<1][2]; int cnt[N<<1]; int max_dis,min_dis; LL ans; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w; } void get_size(int x,int fa) { siz[x]=1; mx[x]=0; for(int i=front[x];i;i=nxt[i]) if(!vis[to[i]] && to[i]!=fa) { get_size(to[i],x); siz[x]+=siz[to[i]]; if(siz[to[i]]>mx[x]) mx[x]=siz[to[i]]; } } void get_root(int x,int pa,int fa) { mx[x]=max(mx[x],siz[pa]-siz[x]); if(mx[x]<min_size) min_size=mx[x],root=x; for(int i=front[x];i;i=nxt[i]) if(to[i]!=fa && !vis[to[i]]) get_root(to[i],pa,x); } void get_sum(int x,int fa,int d) { if(d>max_dis) max_dis=d; if(d<min_dis) min_dis=d; cnt[d+n]++; if(cnt[d+n]==1) g[d+n][0]++; else g[d+n][1]++; for(int i=front[x];i;i=nxt[i]) if(!vis[to[i]] && to[i]!=fa) get_sum(to[i],x,d+val[i]); cnt[d+n]--; } void work(int x,int pa) { min_size=n+1; get_size(x,0); get_root(x,x,0); int maxn=-n,minn=n; for(int i=front[root];i;i=nxt[i]) if(!vis[to[i]]) { max_dis=-n; min_dis=n; get_sum(to[i],root,val[i]); if(max_dis>maxn) maxn=max_dis; if(min_dis<minn) minn=min_dis; ans+=g[n][1]; ans+=g[n][0]*f[n][0]; for(int j=min_dis;j<=max_dis;++j) ans+=g[j+n][0]*f[-j+n][1]+g[j+n][1]*f[-j+n][0]+g[j+n][1]*f[-j+n][1]; for(int j=min_dis;j<=max_dis;++j) { f[j+n][0]+=g[j+n][0]; f[j+n][1]+=g[j+n][1]; g[j+n][0]=g[j+n][1]=0; } } for(int i=minn;i<=maxn;++i) f[i+n][0]=f[i+n][1]=0; vis[root]=true; int rt=root; for(int i=front[root];i;i=nxt[i]) if(!vis[to[i]]) work(to[i],rt); } int main() { // freopen("data.in","r",stdin); // freopen("my.out","w",stdout); read(n); int u,v,w; for(int i=1;i<n;++i) { read(u); read(v); read(w); if(!w) w=-1; add(u,v,w); } work(1,0); cout<<ans; }
3697: 采药人的路径
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1505 Solved: 515
[Submit][Status][Discuss]
Description
采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。
Input
第1行包含一个整数N。
接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。
Output
输出符合采药人要求的路径数目。
Sample Input
7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1
Sample Output
1
HINT
对于100%的数据,N ≤ 100,000。