CSP-S 18
9.8
100+0+20+0
废话
赛时t1先打的暴搜,大数据直接飞了,折半搜即过,30min(当时感觉写的挺快应该不会很差)。
t2先模样例,哎怎么不对?哦少考虑情况。哎怎么还是不对?啊乘写成加了...
各种唐诗错误开始发力了,遂先开t3。
t3一眼出暴力,想了半小时优化,毫无成效...
打个暴力还锅了半天(set的*加不加唐了🤪)。
t4一看又是期望(此时剩余1h左右),暴力都没写折返回t2想拼暴力30pts。
紧张刺激暴力ing
还剩5min,怎么还有一个不对,哎呀又少考虑情况🤣。
最终暴力也没出来。😡😡😡
题解
t1 code(虽然我感觉没啥放的必要,不是人均过吗)
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, up, ans, tot;
int a[10];
int ans1[1 << 20];
inline int km(int a, int b)
{
int ans = 1;
while (b)
{
if (b & 1)
ans *= a;
a *= a;
b >>= 1;
}
return ans;
}
inline int mid(int k)
{
int ans, l = 1, r = tot;
while (l <= r)
{
int mid = (l + r) >> 1;
if (ans1[mid] <= k)
l = mid + 1, ans = mid;
else
r = mid - 1;
}
return ans;
}
void dfs1(int x, int num, int k, int lim)
{
if (x > lim)
{
if (num <= up)
ans1[++tot] = num;
return;
}
if (num + km(a[x], k) > up)
return;
dfs1(x, num, k + 1, lim);
dfs1(x + 1, num + km(a[x], k), 1, lim);
}
void dfs2(int x, int num, int k, int lim)
{
if (x > lim)
{
if (num <= up)
ans += mid(up - num);
return;
}
if (num + km(a[x], k) > up)
return;
dfs2(x, num, k + 1, lim);
dfs2(x + 1, num + km(a[x], k), 1, lim);
}
signed main()
{
// freopen("problem.in", "r", stdin);
// freopen("problem.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> up;
int cnt, num;
for (int i = 1; i <= n; ++i)
cin >> a[i];
dfs1(1, 0, 1, n >> 1);
sort(ans1 + 1, ans1 + 1 + tot);
dfs2((n >> 1) + 1, 0, 1, n);
cout << ans;
return 0;
}
t2 就得好好说说了。
如果把期望去掉
那直接秒了,树上差分即可(虽然我喜欢树剖做)
加上期望后仍然考虑树上差分。
\(ans=E(dep(u))+E(dep(v))-2\times lca(dep(u,v))\)
显然的是:三部分贡献独立,依此求出累加即可。
这里令 \(u\leq v\) .
若 \(u=v\) ,直接特判0.
否则开始处理。
记 \(sum_i\) 表示 \(a_i\) 的前缀和。
令 \(dep_u\) 表示 \(E(dep(u))\).
考虑求 \(dep_u\) .
\(dep_u=\frac{\sum_{i=1}^{u-1}dep_i\times a_i}{sum_{u-1}}+b_i\)
就是“之前期望和+当前贡献”(线性求期望)的形式。
再考虑求 \(lca(dep(u,v))\) .
令 \(lca_u\) 表示 \(u\) 为 \(u,v\) 的 LCA 时的期望。
转移式:
\(lca_u=\frac{\sum_{i=1}^{u-1}lca_i\times a_i}{sum_u}+\frac{dep_u\times a_u}{sum_u}\)
还有一种情况:\(lca(u,v)\) 是 \(u\) 的祖先。
令 \(lcf_x\) 表示 \(x=lca(u,v)\) 且 \(x\) 为 \(u\) 的祖先。
转移式:
\(lcf_x=\frac{\sum_{i=1}^{x-1}lca_i\times a_i}{sum_x}+\frac{(dep_x-b_x)\times a_x}{sum_x}\)
最终结果:
\(ans=2\times (dep_u+dep_v-lca_u-lcf_u)-b_u-b_v)\)
一些注意事项:
观察到转移式均为 \(O(n^2)\).
发现可以前缀和优化,于是将至 \(O(n)\).
\(ans\) 中乘2:
自己手模一棵树,观察易得:链两端的节点只会产生一次贡献,而中间部分会影响上下,相当于产生两次贡献(此种说法并不严谨,只是易于理解,建议动手画图),因此乘2.
最终减去 \(b_u,b_v\) :
我的写法是将边权赋成点权,因此 \(u,v\) 部分算重了,故减去。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
const int N = 3e5 + 10;
int n, q;
int a[N], b[N], sum[N], dep[N], lca[N], lcfa[N];
inline int km(int a, int b)
{
int ans = 1;
while (b)
{
if (b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
signed main()
{
freopen("name.in","r",stdin);
freopen("name.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i], sum[i] = sum[i - 1] + a[i];
for (int i = 1; i <= n; i++)
cin >> b[i], sum[i] = km(sum[i], mod - 2);
dep[1] = b[1]; // 转为点权(类比)
int num = dep[1] * a[1] % mod; // dep:期望
for (int i = 2; i <= n; i++)
{
dep[i] = (num * sum[i - 1] % mod + b[i]) % mod; // 转移式均类似于:之前期望累积和+当前贡献
num = (num + dep[i] * a[i] % mod) % mod;
}
lca[1] = b[1]; // lca_u:u为lca(u,v)的期望
num = lca[1] * a[1] % mod;
for (int i = 2; i < n; i++)
{
lca[i] = (num * sum[i] % mod + dep[i] * a[i] % mod * sum[i] % mod) % mod;
num = (num + lca[i] * a[i] % mod) % mod;
}
lcfa[1] = 0; // lcfa:两点lca为此结点祖先的期望
num = lcfa[1] * a[1] % mod;
for (int i = 2; i < n; i++)
{
lcfa[i] = (num * sum[i] % mod + (dep[i] - b[i]) * a[i] % mod * sum[i] % mod) % mod;
num = (num + lcfa[i] * a[i] % mod) % mod;
}
int u, v;
while (q--)
{
cin >> u >> v;
if (u == v)
{
cout << 0 << "\n";
continue;
}
if (u > v)
swap(u, v);
int ans = (2 * (((dep[u] + dep[v]) % mod - lca[u]) % mod - lcfa[u]) % mod - b[u] - b[v]) % mod;
cout << (ans + mod) % mod << "\n";
}
return 0;
}
t3
咕
t4
咕
本文来自博客园,作者:HS_fu3,转载请注明原文链接:https://www.cnblogs.com/HS-fu3/p/19153714

浙公网安备 33010602011771号