2021牛客多校第五场
B:Boxes
概率签到,根据样例找找规律,显然的结论的min(sum, c + x)
减去x后发现好像是 \(\sum_{i=1}^{n-1} \frac {x_i} {2^i}\) 这个样子的,猜猜结论最小的期望,那么不就是先拿花费小的,搞个前缀和好像和样例对的上
有人害我!!!
void run() {
int n = rd();
double c, sum = 0; scanf("%lf", &c);
for(int i = 0; i < n; ++ i) {
scanf("%lf", &w[i]);
sum += w[i];
}
sort(w, w + n);
for(int i = 1; i <= n; ++ i) pre[i] = pre[i - 1] + w[i-1];
double ans = 0, mul = 2.0;
for(int i = n - 1; i >= 1; -- i, mul *= 2) ans += pre[i] / mul;
printf("%.10lf\n", min(sum, ans /* sum / n*/ + c));
}
D : Double Strings
简单画一画样例就会发现和组合数有关系
对于 \(s1\) 中每一位,枚举 \(s2\) 中每一位,如果该位大于它,显然会产生贡献
贡献的形式是 \(s1_i < s2_j~ -》 ~x * \sum_{k=0}^{min(len1-i, len2-j)} C(len1, k) * C(len2, k)\)
后面的那项明显是一个卷积,有组合数范德蒙特卷积 \(\sum_{i=0}^n C(m1, i)*C(m2, n-i) = C(m1 + m2, n)\)
所以转为 \(\sum_{i=0}^{x} C(x, i)*C(y, i) = \sum C(x, x - i)*C(y, i) = C(x + y, x)\)
而前面的系数是前面 每位相同子序列的个数,画了画样例,发现了递推关系,\(x_{i, j} = x_{i, j - 1} + x_{i - 1, j} - (s1_i != s2_j) * x_{i - 1, j - 1}\)
原理是对于当前项的值,是前项和上一轮的项贡献来的,但是两者有重叠部分,重叠部分是前项的上一轮,而当 \(s1_i = s2_j\) 时,会新增加项,新增加的是当前项 \(s1_i = s2_j\) 必选的一些情况,那么其实就是 前项的上一轮后面加上 \(s1_i = s2_j\),那么就抵消了,不需要减去了,所以我们得到的结论就是求 a中前i个字符和b中前j个字符,相同的子序列个数是 \(dp[i][j] = dp[i][j - 1] + dp[i - 1][j] - (a_i != b_j) * dp[i - 1][j - 1]\)
const int maxn = 1e4 + 10;
const int mod = 1e9 + 7;
int fac[maxn], ifac[maxn], inv[maxn];
inline int C(int n, int m) {
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
inline void init() {
fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = 1;
for(int i = 2; i < maxn; ++ i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
ifac[i] = ifac[i - 1] * inv[i] % mod;
}
return ;
}
//范德蒙特卷积:\sum_{i=0}^n C(m1, i)*C(m2, n-i) = C(m1 + m2, n)
//所以:\sum_{i=0}^{x} C(x, i)*C(y, i) = \sum C(x, x - i)*C(y, i) = C(x + y, x)
inline void qmod(int &a) {
(a >= mod) && (a -= mod);
}
char s1[maxn], s2[maxn];
int num[2][maxn];
void run() {
init();
scanf("%s %s", s1 + 1, s2 + 1);
int len1 = strlen(s1 + 1), len2 = strlen(s2 + 1);
int ans = 0;
for(int i = 0; i <= len2; ++ i) num[0][i] = 1;
for(int i = 1; i <= len1; ++ i) {
int id = (i & 1) ^ 1;
for(int j = 1; j <= len2; ++ j)
if(s2[j] > s1[i])
qmod(ans += num[id][j - 1] * C(len1 - i + len2 - j, len1 - i) % mod);
num[id ^ 1][0] = 1;
for(int j = 1; j <= len2; ++ j) {
qmod(num[id ^ 1][j] = num[id ^ 1][j - 1] + num[id][j]);
if(s2[j] != s1[i]) qmod(num[id ^ 1][j] -= num[id][j - 1] - mod);
}
}
printf("%lld\n", ans);
return ;
}
signed main() {
run();
return 0;
}
D: Greater Integer, Better LCM
题目的意思就是给你两个数,\(x\) ,\(y\),求最小的大于 \(x,y\) 的 \(x‘, y’\), 满足 \(lcm(x', y') = c\)
显然我们需要枚举每种因子在哪个数中,枚举因子子集的复杂度是 \(2^n\)
然后要求出当前有这个因子的大于 \(x, y\) 的最小数,直接暴力dfs枚举所有情况带上外面枚举子集复杂度会达到 \(3^n\)
主要是递归复杂度太大了,那么考虑减小枚举复杂度,就有折半搜索,将所有因数的相乘处理成两部分,分别排序,那么枚举两者的乘积即可,非递归加上有序剪枝就可以过了
char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int maxn = 1e4 + 10;
const int mod = 1e9 + 7;
inline __int128 ksm(__int128 a, int b) {
__int128 res = 1;
while(b) {
if(b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
void put(__int128 x) {
if(x > 9) put(x / 10);
putchar(x % 10 + '0');
}
int n;
__int128 bit[1 << 18];
__int128 a[2], mul[2];
struct node{
int p, q;
__int128 pr;
}p[1 << 18];
node ned[2][20]; int cnt[2];
void out(__int128 x, string s) {
cout << s;
put(x); puts("");
}
__int128 get(__int128 x, int pos) {
static __int128 lps[1 << 18], rps[1 << 18], m;
int cntl = 0, cntr = 0; lps[0] = 1; rps[0] = 1;
for(int i = 0; i * 2 <= cnt[pos] - 2; ++ i) {
for(int j = 0, up = cntl; j <= up; ++ j) {
m = ned[pos][i].p;
for(int k = 1; k <= ned[pos][i].q; ++ k, m *= ned[pos][i].p)
lps[++ cntl] = lps[j] * m;
}
}
for(int i = cnt[pos] - 1; i * 2 > cnt[pos] - 2; -- i) {
for(int j = 0, up = cntr; j <= up; ++ j) {
m = ned[pos][i].p;
for(int k = 1; k <= ned[pos][i].q; ++ k, m *= ned[pos][i].p)
rps[++ cntr] = rps[j] * m;
}
}
sort(lps, lps + cntl + 1);
sort(rps, rps + cntr + 1);
__int128 res = lps[cntl] * rps[cntr] - x;
for(int i = 0, j; i <= cntl; ++ i) {
for(j = 0; j <= cntr && lps[i] * rps[j] < x; ++ j);
if(j <= cntr) res = min(res, lps[i] * rps[j] - x);
}
return res;
}
void run() {
n = rd();
int limit = (1 << n) - 1; bit[0] = 1;
for(int i = 0; i < n; ++ i) {
p[1 << i].p = rd(); p[1 << i].q = rd();
p[1 << i].pr = ksm((__int128)p[1 << i].p, p[1 << i].q);
}
a[0] = rd<__int128>(), a[1] = rd<__int128>();//lcm(a + x, b + y) =
//for(int i = 1; i <= limit; ++ i) bit[i] = bit[i - (i & -i)] * p[(i & -i)].pr;
__int128 ans = -1;
for(int i = 0; i <= limit; ++ i) {
//__int128 x = bit[i], y = bit[limit - i];//-> x >= a, y >= b
cnt[0] = cnt[1] = 0; mul[0] = mul[1] = 1;
for(int j = 0; j < n; ++ j) {
if(!((1 << j) & i)) ned[0][cnt[0]++] = p[1 << j]; mul[0] *= p[1 << j].pr;
else ned[1][cnt[1]++] = p[1 << j]; mul[1] *= p[1 << j].pr;
}
__int128 _x = (a[0] + mul[1] - 1) / mul[1] * mul[1];
__int128 _y = (a[1] + mul[0] - 1) / mul[0] * mul[0];
__int128 tmp = _x - a[0] + _y - a[1];
tmp += get(_x / mul[1], 0) * mul[1];
tmp += get(_y / mul[0], 1) * mul[0];
if(ans == -1) ans = tmp;
else ans = min(ans, tmp);
}
put(ans);
return ;
}
这道题换个思路,用状压的思维去写的话会比较简单
不考虑每种因子按堆来看,独立来看,那么我们枚举的状态是每种因子的状态
这样我们的状压就可以派上用场了,通过状压预处理出来每个状态大于 \(a\) 和 \(b\)的最小花费
那么接下来再枚举 \(a\) 的每个状态 \(i\),\(b\) 就是 \(limit^i\),那么只需要判断一下这个状态的合法性就行了,合法性就是必须要连续的一种因子要么全拿,要么全不拿
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
#define _int __int128
#define inf 0x3f3f3f3f//1061109567
#define infl 0x3f3f3f3f3f3f3f3f//4557430888798830399
#define _inf _int(infl*2) * _int(infl*2)//83080705224710788855960234521465996804
void put(_int x) {
if(x > 9) put(x / 10);
putchar(x % 10 + '0');
}
char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
#define dbg(x...) \
do { \
cout << #x << " -> "; \
err(x); \
} while(0)
void err() {
cout << endl;
}
template<class T, class ...Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << " ";
err(args...);
}
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
_int bit[1 << 18], fa[1 << 18], fb[1 << 18], a, b;
int c[20], m, mul[1 << 18];
bool check(int s) {
for(int i = 1; i < m; ++ i)
if(c[i] == c[i - 1] && (bool(s & (1 << i)) != bool(s & (1 << i - 1)))) return false;
return true;
}
//
void run() {
int n = rd();
for(int i = 0; i < n; ++ i) {
int p = rd(), q = rd();
while(q--) c[m++] = p;
}
for(int i = 0; i < m; ++ i) mul[1 << i] = c[i];
int limit = (1 << m) - 1; bit[0] = 1;
memset(fa, 0x3f, sizeof fa); memset(fb, 0x3f, sizeof fb);
a = rd<_int>(); b = rd<_int>();
for(int i = 0; i <= limit; ++ i) {
if(i) bit[i] = bit[i - (i & -i)] * mul[(i & -i)];
if(bit[i] >= a) fa[i] = bit[i] - a;
if(bit[i] >= b) fb[i] = bit[i] - b;
}
for(int i = limit; i >= 1; -- i)
for(int j = 0; j < m; ++ j) if(i & (1 << j)) {
fa[i ^ (1 << j)] = min(fa[i], fa[i ^ (1 << j)]);
fb[i ^ (1 << j)] = min(fb[i], fb[i ^ (1 << j)]);
}
_int ans = fb[limit] + fa[limit];
for(int i = 0; i <= limit; ++ i) {
if(!check(i)) continue;
ans = min(ans, fa[i] + fb[limit ^ i]);
}
put(ans); puts("");
}
signed main() {
run();
return 0;
}

浙公网安备 33010602011771号