I Infection

知识点: 树上背包

第一次写树上背包的题目,没想到就是在区域赛中

神奇的是树上背包的复杂度,看起来是\(O(n^3)\),但是实际计算只有\(O(n^2)\)

学会树上背包后可以很明显[1]的发现这是一道树上背包的题目

而我认为该题思路的难点[2]在于想到先枚举被感染的点集,再确定起始感染点是哪一个

所以我们就可以定义dp状态:

1.该点集是否包含起始感染点

2.以u为根的子树

3.该点集内被感染的点数

如果已经学过树上背包,此时就十分好转移了


#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define ll long long
template<class T> using vc = vector<T>;
template<class T> using vvc = vc<vc<T>>; const int mod = 1e9 + 7;
const int N = 2e3 + 5; ll dp[2][N][N]; ll ksm(ll x, int n)
{
ll ret = 1;
while (n)
{
if (n & 1) ret = ret * x % mod;
x = x * x % mod;
n >>= 1;
}
return ret;
}
vc<int> h[N];
ll p[N], w[N], ans[N];
int sz[N]; void dfs(int u, int fa)
{
dp[0][u][0] = 1 - p[u]; //未选择初始感染点,u子树,0个感染点
dp[0][u][1] = p[u]; //未选择初始感染点,u子树,1个感染点
dp[1][u][1] = w[u]; //选择u为初始感染点,1个感染点 sz[u] = 1; //已合并节点个数 for (auto v : h[u])
{
if (v == fa) continue;
dfs(v, u); vc<ll> dp0(sz[u] + sz[v] + 1, 0), dp1(sz[u] + sz[v] + 1, 0);
rep(i, 1, sz[u]) rep(j, 0, sz[v])
{
dp0[i + j] = (dp0[i + j] + dp[0][u][i] * dp[0][v][j]) % mod;
dp1[i + j] = (dp1[i + j] + dp[1][u][i] * dp[0][v][j] + dp[0][u][i] * dp[1][v][j]) % mod;
}
sz[u] += sz[v];
//更新dp状态
rep(i, 1, sz[u])
{
dp[0][u][i] = dp0[i];
dp[1][u][i] = dp1[i];
}
}
//统计答案
rep(i, 1, sz[u]) ans[i] = (ans[i] + dp[1][u][i] * (1 - p[fa])) % mod;
} int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
//init
int n; cin >> n;
rep(i, 2, n)
{
int u, v; cin >> u >> v;
h[u].push_back(v);
h[v].push_back(u);
}
ll wi(0);
rep(i, 1, n)
{
int a, b, c;
cin >> a >> b >> c;
wi += w[i] = a;
p[i] = b * ksm(c, mod - 2) % mod;
}
wi = ksm(wi, mod - 2);
rep(i, 1, n) w[i] = w[i] * wi % mod; //树上背包
dfs(1, 0); //输出答案
rep(i, 1, n) cout << (ans[i] % mod + mod) % mod << endl;
return 0;
}

M XOR Sum

知识点: 数位dp

赛时属于是被1e15,1e13这么大的数据范围吓到了

赛后看到了枚举余数后立马想到了累计高位余数

不过我只能想到\(9×9=81\),以为对高位影响最多40

于是就写成了\(dp[45] [20] [45]\),被学长一眼看出不妥

高位余数至少需要\(80+40+20+10+5+2+1=158\)

所以要开\(dp[45] [20] [175]\),所以电子竞技菜是原罪

该题思维的难点在于如何处理如此巨大的 \(n\) ,

而看出通过计算高位余数后,如何处理异或和数位dp就变得十分显然

dp写法

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
template<class T> using vc = vector<T>;
template<class T> using vvc = vc<vc<T>>; const int mod = 1e9 + 7;
const int FN = 25;
ll fac[FN];//阶乘数组
ll ifc[FN];//阶乘逆元
ll inv[FN];//逆元 void init(int n)
{
fac[0] = fac[1] = ifc[0] = ifc[1] = inv[1] = 1;
rep(i, 2, n)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
ifc[i] = ifc[i - 1] * inv[i] % mod;
}
} ll C(ll n, ll m)
{
if (n < m) return 0;
return fac[n] * ifc[m] % mod * ifc[n - m] % mod;
} ll n, m, k;
ll dp[45][20][175];
int lim[45]; int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> k;
init(22); //预处理组合数
rep(i, 0, 40) lim[i] = (m >> i) & 1; //二进制拆分m
memset(dp, -1, sizeof(dp));
if ((n >> 41) > 170)
{
cout << 0 << endl;
return 0;
}
dp[41][k][n >> 41] = 1;
per(i, 41, 1) rep(j, 0, k) rep(t, 0, 170)
{
ll &u = dp[i][j][t]; //当前状态
if (u < 0) continue; //如果当前状态没被枚举过就跳过
int sum = t * 2 + ((n >> (i - 1)) & 1); //上一位遗留来的余数
if (lim[i - 1] == 0) //当前上界为0
{
rep(x, j, k)
{
if (x * (k - x) > sum) continue; //总和n被超过
if (sum - x * (k - x) > 170) continue; //总和n无法到达
ll &v = dp[i - 1][j][sum - x * (k - x)];//被更新状态
if (v < 0) v = 0;
v = (v + u * C(k - j, x - j)) % mod;
}
}
else //当前上界为1
{
rep(j2, 0, j) rep(x, 0, k - j)
{
if ((j2 + x) * (k - j2 - x) > sum) continue; //总和n被超过
if (sum - (j2 + x) * (k - j2 - x) > 170) continue; //总和n无法到达
ll &v = dp[i - 1][j2][sum - (j2 + x) * (k - j2 - x)]; //被更新状态
if (v < 0) v = 0;
v = (v + u * C(j, j2) * C(k - j, x)) % mod;
}
}
}
ll ans(0);
rep(i, 0, k) ans = (ans + (dp[0][i][0] > 0 ? dp[0][i][0] : 0)) % mod;
cout << ans << endl;
return 0;
}

dfs写法

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
template<class T> using vc = vector<T>;
template<class T> using vvc = vc<vc<T>>; ll n, m, k; const int mod = 1e9 + 7;
const int FN = 25;
ll fac[FN];//阶乘数组
ll ifc[FN];//阶乘逆元
ll inv[FN];//逆元 void init(int n)
{
fac[0] = fac[1] = ifc[0] = ifc[1] = inv[1] = 1;
rep(i, 2, n)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
ifc[i] = ifc[i - 1] * inv[i] % mod;
}
} ll C(ll n, ll m)
{
if (n < m) return 0;
return fac[n] * ifc[m] % mod * ifc[n - m] % mod;
} ll dp[45][20][175];
int lim[45]; ll dfs(int w, int num, int sum)
{
if (sum < 0 || sum > 170) return 0;
if (w < 0) return sum ? 0 : 1;
if (~dp[w][num][sum]) return dp[w][num][sum];
ll &u = dp[w][num][sum]; u = 0;
int need = sum * 2 + ((n >> w) & 1);
if (lim[w])
{
rep(i, 0, num) rep(j, 0, k - num)
{
int x = (i + j) * (k - i - j);
u = (u + dfs(w - 1, i, need - x) * C(num, i) * C(k - num, j)) % mod;
}
}
else
{
rep(i, num, k)
{
int x = i * (k - i);
u = (u + dfs(w - 1, num, need - x) * C(k - num, i - num)) % mod;
}
}
return u;
} int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> k;
init(22);
rep(i, 0, 40) lim[i] = (m >> i) & 1;
memset(dp, -1, sizeof(dp));
cout << dfs(40, k, n >> 41) << endl;
return 0;
}

J Math Exam

知识点: 折线模型,容斥

在看完题解后,学长扔过来一道题,说是原题....P3266 [JLOI2015]骗我呢

emmmmmm,怎么说呢,看完题解后,我满脑子都是: 这是人能想到的?

先看这题题面

[JLOI2015]骗我呢

题目描述

说起来,毕业之后 B 君也就见过 R 君两面而已。

R 君有一个 \(n \times m\) 的数组 \(x_{i,j}(1 \le i \le n; 1 \le j \le m)\)。

对于 \(1 \le i \le n; 1 \le j \le m\),满足\(0 \le x_{i,j} \le m\)。求 可能的数组\(x_{i,j}\) 的解数。

B 君觉得限制太宽松,还要求对于 \(1 \le i \le n; 1 \le j<m\),满足 \(x_{i,j} <x_{i,j+1}\),对于\(1 <i \le n; 1 \le j<m\),满足 \(x_{i,j} <x_{i-1,j+1}\)。

B 君认为 R 君可以直接 pwn 掉这个题。

R 君说:「黑的实在逼真 =.=,你起码把解数模 \(10^9+7\) 吧。」B 君觉得 R 君说的有道理,于是想让你求解数模 \(10^9+7\) 的结果。

输入格式

一行两个整数表示 \(n, m\),含义如题目中所述。

输出格式

一行一个数表示同时满足 B 君和 R 君的条件 \(x_{i,j}\) 的解数,模 \(10^9+7\) 的结果。

样例 #1

样例输入 #1

3 3

样例输出 #1

40

提示

对于 \(100\%\) 的数据,\(1 \leq m, n \leq 10^6\)

我的水平只能支持我看懂dp和dp的简化

至于后面的模型转换,真的看了我几个小时

第一篇题解和第二篇题解在最后计算答案时开始变的无比抽象

比如我一开始完全没懂第一篇的字符串ABABA....是在干什么

第二篇计算答案这里的\(C_{2n+m+1}^{n}-C_{2n+m+1}^{n+m+2}-C_{2n+m+1}^{n-1}\)就完全不知道在说什么

其实就是没完全理解折线模型,或者说基本没做过相关题目,总结:就是菜

在看到这篇题解的时候才明白,原来是终点关于两条直线的对称点

明白这一点后,后面的容斥就十分自然,真的自然吗QAQ

这篇题解开头说的真的说到我心坎上了:

我人傻了啊……想了一两个小时,想出了个屁……这是什么东西哦……真的离天下之大谱!

理解完思路后,代码反而变得十分好写


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++) const int mod = 1e9 + 7; namespace cnm
{
const int FN = 3e6 + 5;
ll fac[FN];//阶乘数组
ll ifc[FN];//阶乘逆元
ll inv[FN];//逆元 void init(int n)
{
fac[0] = fac[1] = ifc[0] = ifc[1] = inv[1] = 1;
rep(i, 2, n)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
ifc[i] = ifc[i - 1] * inv[i] % mod;
}
}
ll C(ll n, ll m)
{
if (n < m) return 0;
return fac[n] * ifc[m] % mod * ifc[n - m] % mod;
}
}
using cnm::C; using cnm::fac; using cnm::ifc; using cnm::inv; int n, m; ll A(ll x, ll y);
ll B(ll x, ll y); ll A(ll x, ll y)
{
if (x < 0 || y < 0) return 0;
return (C(x + y, x) - B(y + 1, x - 1) + mod) % mod;
} ll B(ll x, ll y)
{
if (x < 0 || y < 0) return 0;
return (C(x + y, x) - A(y - m - 2, x + m + 2) + mod) % mod;
} int main()
{
cin >> n >> m;
cnm::init(n * 2 + m + 1);
ll ans = C(n * 2 + m + 1, n);
ans = (ans - A(n - 1, n + m + 2) + mod) % mod;
ans = (ans - B(n + m + 2, n - 1) + mod) % mod;
cout << ans << endl;
return 0;
}

在解决完学长扔出的题目后,开始尝试解决该题

显然我们能通过\(4S_i = a_i^2 + 2a_i+1\)化简得到

\[a_i=\begin{cases}
-a_{i-1}\\
a_{i-1} + 2
\end{cases}
\]

看完题解后,我们就有了第一步的模型转换

\[b[i] = \frac{|a[i]+1|}{2}
\]

我的理解是:

  • 绝对值为了处理\(a_i=-a_{i-1}\)
  • 除2是为了让步长为1
  • +1可能是为了变成折线模型

此时模型变成了 \(b_i\) 从 \((0,0)\) 出发,每次可以向右上或右下走

但是不能碰到直线 \(y = - 1\) 与 \(y = \frac{m+1}{2} + 1\)

为了变成折线模型,我们重新处理坐标系

将模型转换成 \(b_i\) 从 \((0,0)\) 出发,每次可以向上或者右走

但是不能碰到直线 \(y = x - 1\) 与 \(y = x + \frac{m+1}{2} + 1\)

此时模型就与上一题相似了

当 \(n=5\),\(m=3\) 时如图

显然对于 A,G,H,I,J,B 就只有 H 有 \(C_5^2\) 种情况(包括不合法的情况)

然后对于碰到直线 \(y = x + 3\) 与 \(y = x - 1\) 的情况我们就需要进行容斥去除不合法的路线

于是我就写下了如下代码


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
template<class T> using vc = vector<T>; const int mod = 998244353; namespace cnm
{
const int FN = 1e7 + 5;
int fac[FN];//阶乘数组
int ifc[FN];//阶乘逆元
int inv[FN];//逆元 void init(int n)
{
fac[0] = fac[1] = ifc[0] = ifc[1] = inv[1] = 1;
rep(i, 2, n)
{
fac[i] = (ll)fac[i - 1] * i % mod;
inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
ifc[i] = (ll)ifc[i - 1] * inv[i] % mod;
}
}
ll C(ll n, ll m)
{
if (n < m || m < 0) return 0;
return (ll)fac[n] * ifc[m] % mod * ifc[n - m] % mod;
}
}
using cnm::C; using cnm::fac; using cnm::ifc; using cnm::inv; ll ksm(ll x, ll n)
{
ll ret = 1;
while (n)
{
if (n & 1) ret = ret * x % mod;
x = x * x % mod;
n >>= 1;
}
return ret;
} int n, m; int main()
{
cin >> n >> m;
cnm::init(n);
vc<int> pre(n + 2, 0);
rep(i, 0, n) pre[i + 1] = ((ll)pre[i] + C(n, i)) % mod;
auto sum = [&](int l, int r) -> ll
{
l = max(l, 0);
r = min(r, n);
if (l > r) return 0;
return ((ll)pre[r + 1] - pre[l] + mod) % mod;
};
m = (m + 1) / 2;
ll l = (n - m + 1) / 2, r = n / 2;
ll ans = sum(l, r);
function<ll(int, int)> A, B;
A = [&](int l, int r) -> ll
{
l = n - l - m - 1, r = n - r - m - 1; swap(l, r);
if (l > n || r < 0) return 0;
return (sum(l, r) - B(l, r) + mod) % mod;
};
B = [&](int l, int r) -> ll
{
l = n - l + 1, r = n - r + 1; swap(l, r);
if (l > n || r < 0) return 0;
return (sum(l, r) - A(l, r) + mod) % mod;
};
ans = (ans - A(l, r) + mod) % mod;
ans = (ans - B(l, r) + mod) % mod;
cout << ans << endl;
return 0;
}

但是问题来了,爆空间了,还是在我把4个 longlong 数组改成 int 的情况下爆空间了,所以我认为是栈空间爆了

那么就必须换写法了,我绝对不会说我看的杜老师代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
template<class T> using vc = vector<T>; const int mod = 998244353; namespace cnm
{
const int FN = 1e7 + 5;
int fac[FN];//阶乘数组
int ifc[FN];//阶乘逆元
int inv[FN];//逆元 void init(int n)
{
fac[0] = fac[1] = ifc[0] = ifc[1] = inv[1] = 1;
rep(i, 2, n)
{
fac[i] = (ll)fac[i - 1] * i % mod;
inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
ifc[i] = (ll)ifc[i - 1] * inv[i] % mod;
}
}
ll C(ll n, ll m)
{
if (n < m || m < 0) return 0;
return (ll)fac[n] * ifc[m] % mod * ifc[n - m] % mod;
}
}
using cnm::C; using cnm::fac; using cnm::ifc; using cnm::inv; ll ksm(ll x, ll n)
{
ll ret = 1;
while (n)
{
if (n & 1) ret = ret * x % mod;
x = x * x % mod;
n >>= 1;
}
return ret;
} int n, m; int main()
{
cin >> n >> m;
cnm::init(n);
vc<int> pre(n + 2, 0);
rep(i, 0, n) pre[i + 1] = ((ll)pre[i] + C(n, i)) % mod;
auto sum = [&](int l, int r) -> ll
{
l = max(l, 0);
r = min(r, n);
if (l > r) return 0;
return ((ll)pre[r + 1] - pre[l] + mod) % mod;
};
m = (m + 1) / 2;
ll l = (n - m + 1) / 2, r = n / 2;
ll ans = sum(l, r);
while (true)
{
l = n - l - m - 1, r = n - r - m - 1; swap(l, r);
ans = (ans - sum(l, r) + mod) % mod;
l = n - l + 1, r = n - r + 1; swap(l, r);
ans = (ans + sum(l, r)) % mod;
if (l > n || r < 0) break;
}
l = (n - m + 1) / 2, r = n / 2;
while (true)
{
l = n - l + 1, r = n - r + 1; swap(l, r);
ans = (ans - sum(l, r) + mod) % mod;
l = n - l - m - 1, r = n - r - m - 1; swap(l, r);
ans = (ans + sum(l, r)) % mod;
if (l > n || r < 0) break;
}
cout << ans << endl;
return 0;
}

  1. 当起始感染点被确定后,其它点被感染只有当其父亲被感染时才会被感染

  2. 也可能只是我菜,写的题太少没见过

广州2022CCPC补题的更多相关文章

  1. hdu5017:补题系列之西安网络赛1011

    补题系列之西安网络赛1011 题目大意:给定一个椭球: 求它到原点的最短距离. 思路: 对于一个椭球的标准方程 x^2/a^2 + y^2/b^2 +z^2/c^2=1 来说,它到原点的最短距离即为m ...

  2. 2017河工大校赛补题CGH and 赛后小结

    网页设计课上实在无聊,便开始补题,发现比赛时候僵着的东西突然相通了不少 首先,"追妹"这题,两个队友讨论半天,分好多种情况最后放弃(可是我连题目都没看啊),今天看了之后试试是不是直 ...

  3. 2018 HDU多校第四场赛后补题

    2018 HDU多校第四场赛后补题 自己学校出的毒瘤场..吃枣药丸 hdu中的题号是6332 - 6343. K. Expression in Memories 题意: 判断一个简化版的算术表达式是否 ...

  4. 2018 HDU多校第三场赛后补题

    2018 HDU多校第三场赛后补题 从易到难来写吧,其中题意有些直接摘了Claris的,数据范围是就不标了. 如果需要可以去hdu题库里找.题号是6319 - 6331. L. Visual Cube ...

  5. [数]补题ver.

    上次补题好像把两次训练混在一起了,总之先按时间顺序补完这一次|ू・ω・` ) HDU-6301 不会的东西不能逃避.jpg 红小豆非常讨厌构造题,因为非常不会,并且非常逃避学习这类题,因为总也搞不清楚 ...

  6. 4.30-5.1cf补题

    //yy:拒绝转载!!! 悄悄告诉你,做题累了,去打两把斗地主就能恢复了喔~~~ //yy:可是我不会斗地主吖("'▽'") ~~~那就听两遍小苹果嘛~~~ 五一假期除了花时间建模 ...

  7. ICPC南京补题

    由于缺的题目比较多,竟然高达3题,所以再写一篇补题的博客 Lpl and Energy-saving Lamps During tea-drinking, princess, amongst othe ...

  8. 2018 CCPC 桂林站(upc复现赛)补题

    2018 CCPC 桂林站(upc复现赛)补题 G.Greatest Common Divisor(思维) 求相邻数的差值的gcd,对gcd分解素因子,对所有的素因子做一次遍历,找出最小答案. 几个样 ...

  9. 【cf补题记录】Codeforces Round #608 (Div. 2)

    比赛传送门 再次改下写博客的格式,以锻炼自己码字能力 A. Suits 题意:有四种材料,第一套西装需要 \(a\).\(d\) 各一件,卖 \(e\) 块:第二套西装需要 \(b\).\(c\).\ ...

  10. 【cf补题记录】Codeforces Round #607 (Div. 2)

    比赛传送门 这里推荐一位dalao的博客-- https://www.cnblogs.com/KisekiPurin2019/ A:字符串 B:贪心 A // https://codeforces.c ...

随机推荐

  1. 开发个RTMP播放器居然这么难?RTMP播放器对标和考察指标

    好多开发者提到,RTMP播放器,不知道有哪些对标和考察指标,以下大概聊聊我们的一点经验,感兴趣的,可以关注 github: 1. 低延迟:大多数RTMP的播放都面向直播场景,如果延迟过大,严重影响体验 ...

  2. KingbaseES V8R3集群运维案例之---用户自定义表空间管理

    ​案例说明: KingbaseES 数据库支持用户自定义表空间的创建,并建议表空间的文件存储路径配置到数据库的data目录之外.本案例复现了,当用户自定义表空间存储路径配置到data下时,出现的故障问 ...

  3. centos7设置时间和上海时区并进行同步

    1.设置时区(同步时间前先设置) timedatectl set-timezone Asia/Shanghai 2.安装组件 yum -y install ntp systemctl enable n ...

  4. 【设计模式】Java设计模式 - 桥接模式

    [设计模式]Java设计模式 - 桥接模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  5. 【gRPC】C++下使用CMakeLists快速构建项目

    在gRPC中,编写.proto文件(protocol buffer文件)来定义RPC服务的接口是第一步 先通过proto的代码生成器编译生成pb.h.pb.cc.grpc.pb.h.grpc.pb.c ...

  6. Elasticsearch:Java 运用示例

    在今天的文章中,我们来介绍如何使用Java来访问Elasticsearch. 首先,我们必须在我们的系统中安装Elasticsearch. Maven 配置 针对Java的开发,我们必须在pom.xm ...

  7. 第一章:模型层 - 10:不返回QuerySets的API

    以下的方法不会返回QuerySets,但是作用非常强大,尤其是粗体显示的方法,需要背下来. 方法名 解释 get() 获取单个对象 create() 创建对象,无需save() get_or_crea ...

  8. Python中class内置方法__init__与__new__作用与区别探究

    背景 最近尝试了解Django中ORM实现的原理,发现其用到了metaclass(元类)这一技术,进一步又涉及到Python class中有两个特殊内置方法__init__与__new__,决定先尝试 ...

  9. 记录一次Bitbucket鉴权的坑

    目录 发生了什么 什么原因 如何解决 总结 发生了什么 今天首次在Fedora上使用git,因为没有小王八(TortoiseGit)帮助,其过程异常焦灼-- 反正经过一系列折腾,我在本地新建了一个项目 ...

  10. vue中修改滚动条样式

    这是一个在VUE中的设置滚动条样式的方法,该方法只适用于Chrome浏览器,亲测在火狐不适用 样式写在 APP.vue 中,可以用在全局,如果只用在那个盒子中,只需要在::前面加上盒子的class名就 ...