T1:这一题我当场就想出正解了,但是因为C++自带快排函数的问题0分。
设s[i][j]表示第i组数有j个数和它相同的组的个数,这个可以用枚举位置+快排来求。
然后就是要对s进行去重。具体做法就是s[i][j]=s[i][j]-s[i][j+1]*(j+1)(j=5~0)。至于为什么要这样做,我们可以想一想,一个j长度+1的相同组就可以产生j+1个长度为j的相同组(C(j+1,j)=j+1),所以我们减去就好了。
总结:C++的快排要一个一个手动比较,不能用for循环。
T2:这题要先选一个唯一的根。具体做法就是找出树的直径(长度最长的链),然后如果直径的长度是奇数的话,那么root就为直径的中点,否则要新加一个点作为root,接着要把直径中间的两个点的连边断开,并把这两个点连向root。
确定了root之后,我们要把每一棵子树分类,同构的标相同的编号,不同的标不同的编号。具体做法就是bfs从下到上处理,每一次把这棵子树的所有儿子的编号记录下来,然后排序,并且判断一下之前有没有与其具有相同儿子序列的点,有就表上相同的号,否则就新建一个种类。
把所有子树都分好类之后我们就可以开始dp+网络流了。
还是从下到上做。设f[i][j]表示以i为根的子树与以j为根的子树匹配的最小代价。
注意:1、i和j要同构(编号相同)才能匹配。2、i和i是可以进行匹配的。
我们每次算i、j匹配的最小代价时,把i和j的所有儿子列成两列(形成一个二分图),然后把同构的儿子连上边(流量为1,费用为f[x][y],x是i的儿子,y是j的儿子)。然后建立源点和汇点,做一遍费用流。设最小费用为mi,那么f[i][j]=mi+(be[i]^fi[j])(be与fi分别为初始状态和密码状态)。最终的答案就是f[root][root]。
总结:1、为了排除一些简单错误,可以通过不停地用生成数据函数(make_data)、暴力程序和正解来对拍。
2、费用流反向弧的费用=正向弧的费用的相反数。不要忘记了。
T3:这题是一道复杂的计算几何。
首先我们知道两个点的管理范围的分界线就是它们的中垂线。所以我们要求出一个点的管理范围,那么就做出它与其他点的中垂线,然后求这些中垂线的半平面交。至于要求最小的被监视的人数,那么我们可以把管理范围有临边的两个点之间连边,设置离初始位置最近的点为起点,靠边界的点为终点,跑一遍最短路就行了。
至于求半平面交,具体见我的“向量”和“半平面交”这两篇博客。