题意:有 n n 种物品,每种物品无限多,每次询问是否能把容量为 wi w i 的背包装满,要求所选的物品中体积不小于 L L 的物品不能超过 c c 件。
n<=50,wi<=1018,c<=30,L<=20000,Vi<=10000 n <= 50 , w i <= 10 18 , c <= 30 , L <= 20000 , V i <= 10000
设最小的物品体积为 z z ,如果 z>L z > L ,那么直接 bitset 优化暴力背包。
否则,注意到,类似墨墨的等式,那么如果能满足 D D ,那么 D+z D + z 也能满足。因此答案对 z z 取模是单调的。
设 f[i][j][k] f [ i ] [ j ] [ k ] 表示考虑前 i i 个物品,大物品用了 j j 个,能装满的模 z z 等于 k k 的最小背包是多大。可以列出转移方程
题意:求复数的模最大生成树
n<=50,m<=100 n <= 50 , m <= 100
考虑如果我们知道了最后复数和的方向,我们就可以把每条边投影在这个方向上,做最大生成树。
考虑怎么解决这个方向。
注意到求最大生成树的时候最后哪些边在树上只取决于边的相对大小,因此随着方向的变化,最大生成树上的边有变化的分界线最多只有 n2 n 2 个。
因此我们算出这些分界线,在一个区域里随便选一个方向就好了。
复杂度 O(m3logm) O ( m 3 log m )
题意:zyh 独自一人在街上漫步,他相信不久后应该可以和 lyh 一起漫步。zyh 有一个 n∗n n ∗ n 的爱情号码牌,他可以从中选出若干个元素,使得每行每列都有奇数个数被选中,且选中的数的乘积是完全平方数。每当他选出这样的数,就可以与 lyh 一起漫步。于是,zyh 想知道对于一个号码牌有多少种选择的方法。对 1000000007 取模。
设 x[i][j]=0/1 x [ i ] [ j ] = 0 / 1 表示 (i,j) ( i , j ) 这个数选不选。由于每行每列必须选奇数个,可以列出 2n 个亦或方程组。然后考虑第 k k 个质因数,记 a[i][x][y]=1/0 a [ i ] [ x ] [ y ] = 1 / 0 表示它在 (x,y) ( x , y ) 这个数中出现了 奇数/偶数 次。同样可以列出一个方程。然后解亦或方程组。
单调队列优化多重背包
如果当前物品体积为 V V ,那么就将体积 mod V V 相同的一起转移。
题意:求一个连通图从某个点 A A 出发回到 A A 的最短的不重复经过一条边的路径。
考虑一条合法的路径,一定是从 A A 出发走一条边,再从另一个端点不经过这条边走最短路回到 A A 。这样我们就有了一个 O(n2logn) O ( n 2 log n ) 的暴力。
如何优化呢?发现每次删除一条边太傻了。我们可以将所有 A A 出边分成两半,删除左边一半,然后跑一次最短路,这样我们就能找出所有路径两端分别在两半里的环,然后左右递归进行,相当于是在二进制分组。复杂度 O(nlog2n) O ( n log 2 n ) 。
二进制分组思想:假设我们要找某两个数间的最小价值,每次可以查询一些数到一些数之间的最小价值。我们可以查询 log log 次,每次查询所有二进制第 i i 位是 0 的数和是 1 的数之间的答案。发现答案的两个数一定存在一位,在这一位上两个数不同,那么他们就会在这一位的查询上被统计。
题意:每一种材料有三个属性 (a,b,c),a+b+c=1 ( a , b , c ) , a + b + c = 1 ,任意几种原材料可以按任意比例混合得到一种新的材料。给定 n n 种原材料,从中选择 k k 种原材料,经过任意比例混合后可以得到另外给定的 m m 种材料。问 k k 最小是多少。
发现第三种属性是没有用的。因为三个属性和为一。把每一种材料看成一个向量,那么两种材料能合成的材料就是他们连线上的所有向量(系数相加等于1)。多个向量能合成的向量就是这些点构成凸包中的每一个点。
现在问题转化成了平面上至少选择多少点,使得他们构成的凸包能够覆盖所有目标点。显然答案一定是一个凸包。我们逆时针看,如果有两点连成的线段,使得所有目标点都在这条线段的左侧,那么这条线段就可能出现在凸包上,我们把这两个端点连起来,边权为 1。然后我们要在这个图上找一个最小环。
给出 floyd 求最小环的代码:
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) ans=min(ans,dis[i][j]+e[j][k]+e[k][i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
每次更新答案的时候相当于枚举环上编号最大的点 k k ,然后从编号小于等于 k k 的点走回去。