codeforces goodbye 2015 总结
A:...
B:乍一看之下是数位Dp?但是其实只需要先枚举一个二进制满串(即( 1<<(i-1) ))的形式),然后再从这个串中减去一个(1<<j)就可以了,其中j<=(i-2).
C:记录一个二维前缀和. 没了....
D:
一眼Dp.我们设Dp[i][j]为从当前位置i到位置j为一个整体的方案个数.
那么最终答案Ans=sigma(Dp[i][n]) (1<=i<=n) , 那么现在考虑如何Dp.
我们当前Dp[i][j]由Dp[k][i-1]转来,那么首先当(i-1)-k+1<j-i+1时一定可以合法转移.
于是我们记录前缀和c[a][b]表示长度小于等于a并且最后一个分界线划分在b的方案数.
那么Dp[i][j]=c[j-i][i-1]+?check(i,j,x,i-1),其中check函数表示的是将x到(i-1)的数字串
与i到j的数字串比较大小的结果,其中必须保证两个数字串的长度相等.(关于x的值的计算,i-1-x=j-i).如果比较成功那么Dp[i][j]再加上Dp[x][i-1].
现在问题是怎么比较.
关于比较,我们首先可以知道如果两个字符串相同,那么为false,如果不同,我们想办法找到两个串最先不同的位置,再比较这个位置的大小就可以了,这个东西可以通过hash或者预处理实现.
hash在这里就不赘述了。
怎么预处理呢?我们设next[i][j]为从i开头的串与从j开头的串,他们最先在i+next[i][j]与j+next[i][j]不同.如果s[i]!=s[j],那么next[i][j]=0,否则为next[i+1][j+1]+1.
E:
贪心,这里就直接翻译出题人的题解了......我们设x[i]为第i个敌人的战斗力,我方3个人战斗力从小到大为a,b,c.
1.若仅有a+b+c>x,那么x由a+b+c干掉.
2.若b+c>x(a+b+c此时一定大于x,之后不赘述了),那么x由b+c干掉,再让a去干掉他能干掉的最大的敌人.
3.若a+c>x,那么x由a+c干掉,再让b去干掉他能干掉的最大的敌人.
4.若a+b>x,那么x由a+b干掉,再让c去干掉他能干掉的最大的敌人.
5.若c>x,那么如果此时a,b,分别能干掉一个人,那么本轮就a,b,c,一人干一个,否则让a+b去干掉他能干掉的最大的敌人.
提醒:判断需要从5到1,即先当5不满足时再考虑4,不满足时再考虑3.....
G:难死了.......
先普及一下从第i个点到第j个点构成的多边形的面积就是M[i][i+1]xM[i+1][i+2]+M[i+1][i+2]xM[i+2][i+3].....M[j-1][j]xM[j][i].
(M[i][j]为i到j的一条向量.)
首先我们可以知道答案就是sigma(tot-2*small),sigma(tot)可以很快得到.
对于small的求法,我们这样考虑:
先把点的序列翻倍,设s[i][j]为从i到j的点构成的多边形的面积.
我们如果可以对于每一个i,求j使得s[i][j+1]>(tot/2),s[i][j]<=(tot/2),设j为next[i].
那么设f[i]为sigma(s[i][j]) (s[i][j]<=(tot/2)),所以求出所有的f[i]就可以了.
首先我们先证明正确性,(i到next[i])与(next[i]+1到i-1(即next[next[i]+1]==i-1))即为全点集仅当s[i][next[i]]=tot/2时有重复.
然而重复会影响我们对small的求值,因此每出现一对重复,我们将sigma(small)预先减去一个tot/2.
那么现在对于每一个i我们都可以o(n)求出f[i]的值了(貌似是废话...).
又观察到,对于next[i+1]与next[i],有next[i+1]>=next[i],因此我们为什么不把f[]一次性处理出来呢?
于是想到了双端队列.
具体的转移当中,我们需要记录很多东西,其中之一比较重要的就是叉积的前缀和:
因为我们考虑到要从f[i]转移到f[i+1]比较麻烦,但是如果我们有叉积前缀和,这里只用把前缀和减去(j-i)个M[i][i+1]xM[i+1][i+2]就可以了.
(前缀和记录的是:比如从i到j的前缀和,我们只记录M[i][i+1]xM[i+1][i+2]....M[j-2][j-1]xM[j-1][j]就可以了,为什么不记录M[j-1][j]xM[j][i]呢?因为这会影响到前缀和的转移.)
注意事项:
1.虽然原题有模数,但是转移过程中有些是不能取模的(比如总面积tot,任何需要用于比较大小的量都不能取模.)
2.注意记录的前缀和f[]代表的是sigma(s[i][j]) (s[i][j]<=(tot/2)),而不是sigma.
3.叉积前缀和记录的是(M[i][i+1]xM[i+1][i+2]+M[i+1][i+2]xM[i+2][i])+(M[i][i+1]xM[i+1][i+2]+M[i+1][i+2]xM[i+2][i+3]+M[i+2][i+3]xM[i+3][i]).....
H:
考试的时候直接第一个过了Pt,然而是fst的....
我的错误算法是直接贪心,若有一条连接i,j的边,就先看能不能从i位数中取一个数,不能的话再从j位数中取一个.
(居然没有想到最大流,虽然想到了估计也是错的...)
那么正解是怎么做的呢?
我们先构造出整棵树的形态(这是为了保证联通),所谓整棵树的形态,就是给每一个位数的数找一个Father,所有连接这一位数的数都连在这个Father上.
(其实就是构造一棵比较菊花的菊花树,仔细想想就知道这应该是比挂成链更优的.)(仔细想想=我不太会证...)
那么我们先用搜索构建出Father间的连接状态(复杂度(K!))
然后对于每一个当前的状态,如果合法,就跑最大流.
令c[i][j]为连接i位数与j位数的边的数量,我们建边s->now(flow=c[i][j]),now->i(flow=inf),now->j(flow=inf).
再对于每一个i,我们建边i->t(flow=left[i]),left[i]表示i位数剩下的数还有多少个.
如果max_flow(s,t)=(n-1)-(k-1) ,进一趟自己建的图把答案取出来就可以了.
(注意这棵树的总形态必须保证合法....)