23:35打到1:25,,,太困了直接去睡了。。。

题目:http://codeforces.com/contest/1111

题解:http://codeforces.com/blog/entry/64989

A:模拟

B:考虑枚举删除i个,一定是最小的i个,剩下的总共的贡献是:min(k*(n-i),m-i)

  但是要注意的是,i<=m!!,否则不合法,而且可能得到更优的答案

C:分治dp。开始脑残直接递归到底。。。O(nklogk)。一共n层,不二分也可以,每段直接暴力枚举找到划分位置。O(nk)

D:(开始没有注意到所有的字母都要满足全部都出现在某一半。。FST)

显然要先考虑没有(x,y)限制的

分成两个部分考虑问题

如果我们把一些字母都划分到n/2里,剩下的在另一半,那么内部再排列的方案数永远是:W=((n/2)!(n/2)!)/(cnt[a]!*cnt[b]!...cnt[Z]!)

所以,我们只要求把52个字母划分到n/2个位置的方案数

直接0/1背包搞定

最后的ans是dp[n/2]*2*W

 

考虑加上(x,y)的限制

相当于钦定(x,y)必须放在某一半,其他随便放

考虑没有x,y的那一半,所以等价于:不用x,y,填满n/2位置的方案数

就是一个背包上修改的问题

 

考虑总共只有52*52对,不妨预处理ans[i][j]

消掉影响的话,把原来的dp数组copy一份叫做cp,

for(int i=buc[x];i<=n;++i){

  cp[i]-=cp[i-buc[x]]

}

内层枚举y再做一次

然后ans[i][j]=2*cp[n/2]*W

O(n*52*52)

 

 

E:

m<=300,sumk<=1e5,启示我们O(m∑k)

发现,一个点的所有祖先,假设有h[i]个,必然都在不同的集合

所以,如果我们把k个点的h[i]求出来,按照h[i]从小到大排序

dp,设dp[i][j]表示,前i个点,分成j个非空集合的方案数

dp[i][j]=dp[i-1][j]*(j-h[i])+dp[i-1][j-1]

(有点类似第二类斯特林数)

 

考虑求出h[i]

1.如果rt=1,直接dfn序,对子树用线段树处理即可

2.如果rt=r,讨论一下,找到对应的子树(原来的或者取反(两块))处理即可

  也可以避免讨论,其实就是i到Y路径上标记点的个数。h[i]表示rt=1时i到根路径上标记点的数量(祖先数量+1,因为包括自己),hnew[i]=h[i]+h[Y]-2*h[lca(i,Y)]+[mark[lca(i,Y)]==true]-1(减掉i自己)

 

复杂度:O(m∑k+∑klogN)