2022ICPC杭州(ACK)
A
\(exgcd\)
\(exgcd(a,b,x,y)\):\(a,b\)已知,用于求解\(ax + by = gcd(a,b)\)中的某一组解 \(x,y\);而求\(ax + by = k * gcd(a,b)\) 的解,只需要将 \(exgcd(a,b,x,y)\) 求出的\(x,y\) 同时乘上 \(k\) 即可。
C
被复杂题意包装起来的01背包。
简化一下题意就是:有\(n\)个物品,背包体积为\(k\),第\(i\)个物品的体积为\(p[i]\),且有\(p[i]\)种价值,其中只有一种价值对应“全部升级”(价值为\(w[i][p[i]]\)),剩下\(p[i]-1\)种价值对应“部分升级”(价值对应\(w[i][1到p[i]-1]\))。可以选择至多一个物品“部分升级”,其他必须为“全部升级”。设选取的“全部升级”的物品总体积为\(sum\),则“部分升级”物品的价值为\(w[i][k-sum]\)。求可获最大价值。
可以将物品的\(p[i]\)个价值看做:物品的体积为\(j\)时(\(j\in [1,p[i]]\)),价值为\(w[i][j]\)(可以证明这种定义方式与题意等价)。这样,就规定选取的物品存在“部分升级”时,总价值必须恰好为\(k\)。因此在初始化\(dp\)数组时必须为\(-INF\)(对于未规定选取恰好体积时初始化为\(0\))。
特点在于\(p[i]<=10\),且至多只能有一个“部分升级”的物品。所以状态可设置为:
\(dp[i][j][0/1]\):考虑前\(i\)个物品,选取物品的总体积为\(j\),且选了\(0/1\)个“部分升级”的物品,最大总价值。
可以直接暴力枚举“部分升级”物品的所有价值并转移,转移时对于第\(i\)种物品,考虑\(3\)种情况即可:
- 不选第\(i\)个物品(不代表真的不选,而是作为题中\(sum>k\)的那部分,价值为 \(0\) 地选择该物品,相当于不选。这里容易混淆)-> 从前缀的状态转移
- 第\(i\)个物品“全部升级” -> “部分升级”的\(0/1\)状态均能转移
- 第\(i\)个物品“部分升级” -> 由于最多只能选一个,则只能从已选“部分升级”的 \(0\) 状态转移
注意当\(k\)特别大时,所有物品不能装满背包,这时所有物品必须均为“全部升级”状态。而\(dp\)只处理了选取恰好体积的情况,故需要特判\(k\)很大的情况。(如果不特判,而是采用枚举\(dp[n][0到m][0/1]\)的方式更新答案,则会有一种情况导致错误:某个物品“部分升级”比“全部升级”的价值更大,导致对这个物品进行选择时,在背包体积足够时,选了该物品“部分升级”的情况。虽然这样的情况答案更优,但却是不合法的,因为在背包体积足够的情况下,所有物品必须“全部升级”。具体见WA on 7)
可以倒序枚举体积,省去01背包\(dp\)数组中物品个数的那一维。
具体实现见代码
复杂度\(O(nk*10)\)
K
字典树
考虑逆序对的贡献是怎样产生的。
分析后可发现,逆序对产生的方式有两种:
- 两个字符串的前\(i-1\)个字符相同,第\(i\)个字符不同
- 一个字符串是另一个字符串的前缀
设\(f[x][y]\)表示在给定的字符串序列中,\((str1,str2)\)对的数量,其中\((str1,str2)\)满足:
- \(str1\) 在 \(str2\) 之前
- 存在一个位置 \(i\) ,使得\(str1[1到i-1]==str2[1到i-1]\)
- \(str1[i]==x\),\(str2[i]==y\)。\(x,y\)表示字符。
则 $ans1 = $
其中\(seq[x]\)为字符\(x\)在当前给定的字母表中的位置。
计算\(ans2\):在插入某个字符串\(str\)前,用这个字符串跑一遍字典树,若可到\(str\)的末尾,则看其末尾之后的前缀数量,这些字符串都可以使得\(str\)是它们的真前缀,自然和\(str\)构成逆序对。对于每个字符串,在插入前做这样的统计即可。注意走不到末尾时不能作统计,具体实现见代码。