3697: 采药人的路径
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 309 Solved: 112
[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
我的第四道点分。。也是最近点分收尾题。。
看题解前没有思路。。还以为每一个重心就是休息点。。。发现自己真是傻到家了。。
因为这道题出现在同一颗子树中的情况很难处理。。所以可以让当前的子树和已经处理完的子树合并。。
然后我们可以把阴性路径的权值赋为-1,阳性的赋为1,这样求一下两点之间的路径和就可以知道中间的阴阳点的多少了。。。用\(g[i][0..1]\)和\(f[i][0..1]\)分别表示之前的子树和当前子树中权值和为i的点的数量。。0,1分别表示这条路径上有没有休息点。。然后进行合并\(ans+=g[i][1]*f[-i][1]+g[i][0]*f[-i][1]+g[i][1]*f[-i][0]\)。。还要注意如果两边的路径和为0但是没有休息点。。可以把重心作为休息点。。
判断一条路径上有没有休息点的方法是在他到根的路径上找是否有权值和等于他的点。。如果有。。那个店就是休息点。。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 200010; struct data{int x,y,s,next;} e[N<<1]; int t[N<<1],dis[N],vis[N],deep[N],son[N],F[N],x,y,s,n,root,cnt,head[N],sum,mxdeep; long long ans,f[N<<1][2],g[N<<1][2]; int read(){ int x = 0,f = 1,c = getchar(); while (c<'0' || c>'9'){if (c=='-') f = -1;c = getchar();} while (c>='0' && c<='9') x = x*10+c-'0',c = getchar(); return x*f; } void Insert(int x,int y,int s){ e[++cnt].x = x;e[cnt].y = y;e[cnt].s = s; e[cnt].next = head[x];head[x] = cnt; } void getroot(int x,int fa){ son[x] = 1;F[x] = 0; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].y] && e[i].y!=fa){ getroot(e[i].y,x); son[x]+=son[e[i].y]; F[x] = max(F[x],son[e[i].y]); } F[x] = max(F[x],sum-F[x]); if (F[x]<F[root]) root = x; } void getdeep(int x,int fa){ mxdeep = max(mxdeep,deep[x]); if (t[dis[x]]) f[dis[x]][1]++; else f[dis[x]][0]++; t[dis[x]]++; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].y] && e[i].y!=fa){ dis[e[i].y] = dis[x]+e[i].s; deep[e[i].y] = deep[x]+1; getdeep(e[i].y,x); } t[dis[x]]--; } void work(int x){ int mx = 0;vis[x] = 1;g[n][0] = 1; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].y]){ dis[e[i].y] = n+e[i].s; deep[e[i].y] = 1; mxdeep = 1;getdeep(e[i].y,x);mx = max(mx,mxdeep); ans+=(g[n][0]-1)*f[n][0]; for (int j=-mxdeep;j<=mxdeep;j++) ans+=g[n+j][1]*f[n-j][1]+g[n+j][1]*f[n-j][0]+g[n+j][0]*f[n-j][1]; for (int j=n-mxdeep;j<=n+mxdeep;j++){ g[j][0]+=f[j][0];g[j][1]+=f[j][1]; f[j][1] = f[j][0] = 0; } } for (int i=n-mx;i<=n+mx;i++) g[i][0] = g[i][1] = 0; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].y]){ sum = son[e[i].y]; root = 0; getroot(e[i].y,-1); work(root); } } int main(){ n = read(); for (int i=1;i<n;i++){ x = read();y = read();s = read(); if (s) Insert(x,y,1),Insert(y,x,1); else Insert(x,y,-1),Insert(y,x,-1); } sum = n;F[0] = 1e9; getroot(1,-1); work(root); printf("%lld",ans); }
2023年12月14日 16:04
Not everyone knows all information about a celebrity and their net worth which changes every day according to their fame and investment but you can follow the info on idol net worth which is available for everyone.