题意:
给定一棵树,按一正一负一正这样求所有奇数长度链(链有方向)的权值和,(偶数显然会抵消)。
解:
去学了一波点分治,专治求树上各种链的问题,简单口胡一下(
经典引入:求树上所有长度为k的链的权值和。
从根看起,链有两种,跨过根的和不跨过根的,除了叶子节点,不跨过根的它肯定也要跨过些什么,因此所有链都可以劈成两半来加,然后递归处理子树内的情况,此为分治。
要想得到长为k的链,可以把它分成两段,一段长为n,考虑到还有本结点,另一段长k-n-1。于是递归处理子树内长为k的链有多少,权值和是多少,然后排列组合一下加起来。
考虑这种情况:选取的两条链有共同结点时,显然这条链不能选,要把多算的次数减去。于是遍历每一条出边时,计算当前节点会被多算几次,也就是子树内符合要求的数量,减掉它。
再考虑树退化成一条链的情况:从头开始依次遍历,复杂度还是n2,不妙。所以还要每次找子树的重心,然后以子树重心为根,处理剩余的情况。
找子树重心的方法:该结点最大子树的结点数量最少即为重心。
再看这道题,统计奇偶就可以了。也就是说,点分治每次重写cal函数就行,很快乐。
如果权值在点上,根节点单独计算;权值在边上直接算就行。
几个WA的点:
1.找到重心还从1开始遍历;
2.找重心的时候由于剩下部分也是一棵子树,所以应该和 *当前树大小减去包括当前点在内的子树大小* 比较;
3.经典问题 一种基于错误的寻找重心方法的点分治的复杂度分析(问题详见该页评论区
实测错误方法和正确的时间差得真不多(
几乎一模一样
4.由经典问题引发的惨案:在分治程序里要记录当前子树大小,但是如果用正确的方法(totsize=son[to]>son[now]?tot-son[now]:son[to];)算totsize,这个值是要应用到子树里的,因此还要开一个变量保存当前的tot。(其实怪弱智的
5.如果TLE了,多半是重心求错了,(摆烂就好
代码完全参考此博客,但在求重心处改了改:
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 1000000007 #define base 2333 //#define int long long int n; ll a[maxx]; struct edge{ int u,v,next; }v[maxx*2];int head[maxx]={0},cnt=0; void add(int x,int y){ v[++cnt].u=x,v[cnt].v=y,v[cnt].next=head[x],head[x]=cnt; } ll ans=0; int rt=0; int vis[maxx]={0},son[maxx]={0},mx[maxx]={0}; void dfs_rt(int now,int fa,int tot){ mx[now]=0,son[now]=1; for(int i=head[now];i;i=v[i].next){ int to=v[i].v; if(vis[to]||to==fa) continue; dfs_rt(to,now,tot); son[now]+=son[to]; mx[now]=max(mx[now],son[to]); } mx[now]=max(mx[now],tot-son[now]); if(!rt||mx[rt]>mx[now]) rt=now; } ll d[2]={0},num[2]={0}; void dfs_dis(int now,int fa,int n,ll sum){ d[n]=(d[n]+sum)%mod; num[n]++; for(int i=head[now];i;i=v[i].next){ int to=v[i].v; if(vis[to]||to==fa) continue; dfs_dis(to,now,!n,sum+a[to]*(n?1:-1)); } } ll cal1(int now){ d[0]=d[1]=0; num[0]=num[1]=0; dfs_dis(now,now,0,0); d[1]=-d[1]; return (d[0] * num[0] * 2 + d[1] * num[1] * 2 + a[now] * (( num[0] * num[0] - num[1] * num[1])%mod)%mod)%mod; } ll cal2(int now,ll w){ d[0]=d[1]=0; num[0]=num[1]=0; dfs_dis(now,now,1,-a[now]); d[1]=-d[1]; return (d[0] * num[0] * 2 + d[1] * num[1] * 2 + w * (( num[0] * num[0] - num[1] * num[1])%mod)%mod)%mod; } void dfs_work(int now,int tot){ vis[now]=1; ans=(ans+cal1(now))%mod; for(int i=head[now];i;i=v[i].next){ int to=v[i].v; if(vis[to]) continue; ans=(ans-cal2(to,a[now]))%mod; int totsize=son[to]>son[now]?tot-son[now]:son[to]; rt=0;dfs_rt(to,now,totsize); dfs_work(rt,totsize); } } signed main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=n-1;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } rt=0;dfs_rt(1,0,n); dfs_work(rt,n); printf("%lld\n",(ans+mod)%mod); return 0; }