点分治
Orz hzwer
倒是比较好想到点分治……然而在方案统计这里,我犯了两个错误……
1.我比较傻逼的想的是:通过儿子来更新父亲,也就是统计以x为根的子树中xxxx的路径有多少条……这样转移。
然而这实在是太傻逼了,黄学长教做人:从父亲来更新儿子,走到一个节点直接更新路径的统计数,反正我们要的是【经过root的xx路径的数量】
所以可以一遍dfs直接搞出来……
2.统计方案的方式也想错了……我只考虑了以root作为中转站的路径,然而经过root的路径中,并不只有这种路径是合法的……中转站在途中某个点的也可以QwQ
另外感觉黄学长记录[-d,d]的姿势很神啊……直接数组开大一倍,然后转成[n-d,n+d]……
其他的……套模板呗- -
1 //BZOJ 3697 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;++i) 8 #define F(i,j,n) for(int i=j;i<=n;++i) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 #define pb push_back 11 using namespace std; 12 typedef long long LL; 13 inline int getint(){ 14 int r=1,v=0; char ch=getchar(); 15 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 16 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 17 return r*v; 18 } 19 const int N=1e5+10; 20 /*******************template********************/ 21 22 int to[N<<1],nxt[N<<1],head[N],cnt,v[N<<1]; 23 void add(int x,int y,int z){ 24 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; v[cnt]=z; 25 to[++cnt]=x; nxt[cnt]=head[y]; head[y]=cnt; v[cnt]=z; 26 } 27 28 int n,rt,s[N],h[N],size,dep[N],mxdeep; 29 LL ans,g[N*2][2],f[N*2][2]; 30 int t[N<<1],dis[N]; 31 bool vis[N]; 32 33 inline void getroot(int x,int fa){ 34 s[x]=1; h[x]=0; 35 for(int i=head[x];i;i=nxt[i]) 36 if (to[i]!=fa && !vis[to[i]]){ 37 getroot(to[i],x); 38 s[x]+=s[to[i]]; 39 h[x]=max(h[x],s[to[i]]); 40 } 41 h[x]=max(h[x],size-s[x]); 42 if (h[x]<h[rt]) rt=x; 43 } 44 45 inline void dfs(int x,int fa){ 46 mxdeep=max(mxdeep,dep[x]); 47 if (t[dis[x]]) g[dis[x]][1]++; 48 else g[dis[x]][0]++; 49 t[dis[x]]++; 50 for(int i=head[x];i;i=nxt[i]) 51 if (!vis[to[i]] && to[i]!=fa){ 52 dep[to[i]]=dep[x]+1; 53 dis[to[i]]=dis[x]+v[i]; 54 dfs(to[i],x); 55 } 56 t[dis[x]]--; 57 } 58 inline void getans(int x){ 59 int mx=0; 60 vis[x]=1; f[n][0]=1; 61 for(int i=head[x];i;i=nxt[i]) 62 if (!vis[to[i]]){ 63 dis[to[i]]=n+v[i]; 64 dep[to[i]]=1; 65 mxdeep=1; 66 dfs(to[i],x); mx=max(mx,mxdeep); 67 ans+=(f[n][0]-1)*g[n][0]; 68 F(j,-mxdeep,mxdeep) 69 ans+=f[n-j][1]*g[n+j][1]+f[n-j][0]*g[n+j][1]+f[n-j][1]*g[n+j][0]; 70 //f[n][0]+1的原因是要将x作为起点or终点的合法路径(g[n][1])统计进来 71 F(j,n-mxdeep,n+mxdeep) 72 f[j][0]+=g[j][0], 73 f[j][1]+=g[j][1], 74 g[j][0]=g[j][1]=0; 75 } 76 F(i,n-mx,n+mx) f[i][0]=f[i][1]=0; 77 //统计答案↑ 78 for(int i=head[x];i;i=nxt[i]) 79 if (!vis[to[i]]){ 80 rt=0; size=s[to[i]]; 81 getroot(to[i],x); 82 getans(rt); 83 } 84 //继续分治↑ 85 } 86 int main(){ 87 #ifndef ONLINE_JUDGE 88 freopen("3697.in","r",stdin); 89 freopen("3697.out","w",stdout); 90 #endif 91 n=getint(); 92 F(i,2,n){ 93 int x=getint(),y=getint(),z=getint(); 94 if (!z) z--; 95 add(x,y,z); 96 } 97 size=n; h[rt=0]=n+1; 98 getroot(1,0); 99 getans(rt); 100 printf("%lld\n",ans); 101 return 0; 102 }
3697: 采药人的路径
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 252 Solved: 93
[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
Sample Output
1
HINT
对于100%的数据,N ≤ 100,000。