题意
一个 个点的树,点有点权 ,每次选择一个相连的点,每走一次到该点,该点的点权变为 。
可以从任意点开始,任意点结束,能重复经过多次。
问有多少个出发点,满足最后存在一种方案使得点权均为 。
原题:
加强版:。
题解
Part 0:,必须走回出发点
首先必须明白一点,如果一条边的两端点全是 ,我们也还是可以走的。因为来回走 次不就好了嘛。
假设枚举出发点,然后以该出发点为根。看子树内是否满足。
比较像树形DP的形式。
考虑DP的本质,是将一个大问题变成若干个子问题。
能否设置一个状态,表示子树内,除了这个点以外的点全变为 后,该点还要变多少次才能变为 。
转移的时候让父亲和儿子相互抵消,使得儿子变为 ,然后父亲此时的状态可以 计算。
显然是可以的。设为 。
那么有:
最后必须满足 为 , 才能满足条件。
枚举 , 树形DP,计算是否可行。
Part 1:
是否能把上面的思路转移过来呢?
试一下吧。
还是枚举根节点(出发点)。
考虑最后一次经过根节点的情况,如果要满足条件,那么只能剩下一条 的链,且此时根节点为 。
如图所示:

那么可以发现,如果让链上最后两个 相互抵消,那么一直抵消下去,最后最多剩下一个 ,再让这个 和根节点相互抵消,此时根节点变为了 。也就是说,根节点还需要 次才变为 。这种情况也是可以的,即 。
时间复杂度 。
到这里就做完了原题。
Part 2:
显然换根DP就好了。
时间复杂度 ,需要两遍dfs,常数有点大。
Part 3:
考虑转移方程到底是什么:。
假设以 为根节点。
那么处于奇数层的 的贡献是 ,偶数层的 的贡献是 。
而显然偶数层的点的最终答案是一样的。
奇数层的答案是偶数层的相反数。
因此直接暴算就好了,维护一下层数的奇偶性,就dfs也不用了。
这还是树形DP吗?
注意 的特判。
时间复杂度 。
程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <cstdio> #define ll long long inline int read() { int x=0; char ch=getchar(); bool f=0; for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1; for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48); return f?-x:x; } const int N=2e6+5; int n,k,a[N]; ll s1[2],s2[2],ans,tmp; bool d[N]; int main() { n=read(); k=read(); for(int i=1;i<=n;i++) a[i]=read(); if(n==1) return printf("%d",(a[1]?0:1))&0; s1[0]++; s2[0]=a[1]; for(int i=2;i<=n;i++) { d[i]=d[read()]^1; s1[d[i]]+=i,s2[d[i]]+=a[i]; } tmp=(s2[1]%k-s2[0]%k+k)%k; if(tmp==0||tmp==k-1) ans+=s1[0]; if(tmp==0||tmp== 1) ans+=s1[1]; return printf("%lld\n",ans)&0; }
|