看到A发现是个简单题。
看到B认为是个贪心。
看到C觉得挺可做的。
想了一会C,发现只会log拆数的20分。
(后来发现是10分)
看B,猜了一些结论然而都错了。
接着很快就想到了贪心的正确做法(长链剖分)。
而且使用长链长度和=n的条件想到了时间复杂度为log的做法。
想了一会就想到了线性做法。
然而挺难写。
(后来发现算法错了)
写了A,但是调了很久。。。。
最后没时间写B了。
如果我调A没调这么久,则可以想到C的(O(3^n*18*n))做法,获得50分。
看上去还是代码能力不行。。。。
题解:
A:注意到两个长度相同的区间答案一定相同,所以直接记忆化搜索。
B:考虑使用长链剖分。
由于dp不太行所以考虑贪心。
每次选择从根节点到深度(leq d)节点上最长的儿子。
显然这个过程等于求深度(leq d)节点的长链剖分的长度前(l)大的链。
考虑挖掘长链剖分的性质。注意到如果对原树进行长链剖分,则每个节点的重儿子在删除深度(leq d)的点之后都不会变化。
考虑一个转化。维护一个元素为区间的集合(S)。一条长链假设最低节点深度为(l),最高深度为(r),则往(S)内插入([l,r])
考虑一个询问的值(d),我们需要把所有(S)内的元素和([1,d])取交集,假设生成集合(T),我们需要询问这个集合内前(l)大的数。
考虑扫描线。
注意到所有区间的长度和为(O(n))的,所以在扫到当前点的时候可以枚举包含当前所有点的区间,暴力更新长度。
在线段树上二分即可得到一个(O(nlog_2n))的做法。
注意到我们的问题比原来更弱,我们只需要支持单点把值+1。
而且由于每一个点只会+,不会-,所以会发现能够贡献答案的值的下界是单调不降的
使用桶维护即可。
不知道为什么调了这么久。
C:想到了除了计算(m=0)外的所有部分。
但是由于只想到了计算(m=0)的(O(3^n*18*n))做法,只能拿50分。
先写原来的做法。
先考虑(m=0)。
使用类似数位dp的转化:把一个数拆成(log_2C)个数。
比如把(110111)拆成(0xxxxx),(10xxxx),(1100xx),(11010x),(11011x)
从大到小遍历每一位。
设(f_{i,s})表示遍历到第(i)位,我们统计过(s)集合内的方案数,且前面(i)位的xor和=(C)的前(i)位。
枚举新统计答案的位进行转移。
时间复杂度(O(3^n*18))
(m
eq 0)考虑容斥,枚举打破哪些限制。
定义等边:被打破限制的边。
这些边所构成的连通块的值都是相同的。
假设一个连通块的权值都为(a_i),则当它的大小为奇数贡献(a_i),否则贡献(0)。
然后对于每个连通块产生的贡献就是一个(m=0)的dp。
定义一个连通块的代表元为:假设一个连通块的权值都为(a_i),则当它的大小为奇数为(a_i),否则为(0)。
考虑怎么计算答案,注意到如果所有集合的代表元相同,则计算系数也相同,设(f_{s,s1})表示(s)集合,代表元集合为(s1)的贡献。
由于(s1)是(s)的子集,所以状态数为(O(3^n))
根据经典的最小数原理,枚举一定包含连通块内值最小的数的集合进行转移。
转移的时候要乘以集合的容斥系数。
由于我们可以在(O(2^n))的时间内计算所有集合的代表元,所以转移时间复杂度为(O(1))。
容易发现做(n)次(f)的dp即可求出答案。
考虑计算集合的容斥系数。
我们需要计算一个连通图中,所有情况的边数(假设为(p)),((-1)^p)的和。
这是个经典的问题。
考虑容斥,设(g_i)表示(i)集合内的点连通的方案数,(h_i)表示不连通的方案数。
容斥的“全部方案”可以如下计算:
构造多项式((x+1)^m),多项式每一个因式((x+1))能够选择(x)或者是(1)。
取(x=-1)可以构造出当前的“全部方案”。
所以我们得到了如果集合不包含任何边,全部方案为((x+1)^0=1),否则为0。
求出集合内包含多少边可以使用fwt。
钦定一个必须包含集合内最小点的集合,强制它与外边不连通进行转移。(转移系数就是(1))
这样子时间复杂度为(O(3^n))。
考虑优化(m=0)的计算。
前面的做法并没有利用到(xor)的性质。
考虑枚举一个数(x),使得它是最大的顶到上界的数。
由于xor的自反性,后面的数可以任意选择,调整(x)总可以调整到符合条件。
考虑dp计算。设(f_{i,0/1,0/1})表示dp到当前这位,xor为0/1,前面是否有数顶到上界。
这样子对于一组(a)时间复杂度降低到(O(nlog_2C))
对每一个集合做一次,时间复杂度为(O(2^nnlog_2C))
总时间复杂度为(O(3^n+2^nnlog_2C))