3697: 采药人的路径
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 681 Solved: 246
[Submit][Status][Discuss]
Description
采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。
Input
第1行包含一个整数N。
接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。
Output
输出符合采药人要求的路径数目。
Sample Input
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1
Sample Output
HINT
对于100%的数据,N ≤ 100,000。
Source
3127: [Usaco2013 Open]Yin and Yang
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 116 Solved: 82
[Submit][Status][Discuss]
Description
Input
Output
Line 1: One integer, representing the number of possible paths Farmer John can choose from.
Sample Input
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1
INPUT DETAILS:
There are 7 barns and 6 edges. The edges from 1 to 2, 2 to 4 and 2 to 5 have Charcolais herds along
them.
Sample Output
OUTPUT DETAILS:
No path of length 2 can have a suitable rest stop on it, so we can only consider paths of length 4.
The only path that has a suitable rest stop is 3-1-2-5-7, with a rest stop at 2.
HINT
Source
Solution
这道题有点厉害
首先肯定想到点分治,问题在于如何统计答案
首先对于阴阳边,我们可以赋权值+1/-1,这样路径阴阳数目就比较直观了
定义$f[i][0/1]$表示当前子树中路径和为$i$的路径个数,0/1表示路径上是否存在前缀为$i$的点
定义$g[i][0/1]$表示之前前子树中路径和为$i$的路径个数,0/1同理
那么对答案的贡献就是$g[0][0]*f[0][0]+\sum (g[-i][0]]*f[i][1]+g[-i][1]*f[i][0]+g[-i][1]*f[i][1])$
要注意考虑下标为负的情况...全部+N即可
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 100010 #define LL long long int N; struct EdgeNode{int next,to,val;}edge[MAXN<<1]; int head[MAXN],cnt=1; void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].val=w;} void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);} int size[MAXN],maxx[MAXN],root,Sz; bool visit[MAXN]; void DFSRoot(int now,int last) { size[now]=1; maxx[now]=0; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) { DFSRoot(edge[i].to,now); size[now]+=size[edge[i].to]; maxx[now]=max(maxx[now],size[edge[i].to]); } maxx[now]=max(maxx[now],Sz-size[now]); if (maxx[now]<maxx[root]) root=now; } int deep[MAXN],maxd,md,mark[MAXN<<1],D[MAXN<<1]; LL f[MAXN<<1][2],g[MAXN<<1][2],ans; void DFS(int now,int last) { maxd=max(maxd,deep[now]); if (mark[D[now]]) f[D[now]][1]++; else f[D[now]][0]++; mark[D[now]]++; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) { deep[edge[i].to]=deep[now]+1; D[edge[i].to]=D[now]+edge[i].val; DFS(edge[i].to,now); } mark[D[now]]--; } void Get(int x,int val) { D[x]=N+val; deep[x]=1; maxd=1; DFS(x,0); md=max(maxd,md); ans+=(g[N][0]-1)*f[N][0]; for (int i=-maxd; i<=maxd; i++) ans+=g[N-i][1]*f[N+i][1]+g[N-i][0]*f[N+i][1]+g[N-i][1]*f[N+i][0]; for (int i=N-maxd; i<=N+maxd; i++) g[i][0]+=f[i][0],g[i][1]+=f[i][1],f[i][0]=f[i][1]=0; } void Divide(int x) { visit[x]=1; g[N][0]=1; md=0; for (int i=head[x]; i; i=edge[i].next) if (!visit[edge[i].to]) Get(edge[i].to,edge[i].val); for (int i=-md; i<=md; i++) g[N+i][0]=g[N+i][1]=0; for (int i=head[x]; i; i=edge[i].next) if (!visit[edge[i].to]) { Sz=size[edge[i].to]; root=0; DFSRoot(edge[i].to,x); Divide(root); } } int main() { N=read(); for (int x,y,z,i=1; i<=N-1; i++) x=read(),y=read(),z=read(),InsertEdge(x,y,z? z:-1); Sz=maxx[root=0]=N; DFSRoot(1,0); Divide(root); printf("%lld\n",ans); return 0; }