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. KingbaseES 的行列转换

    目录 背景 行转列 数据准备 分组聚合函数+CASE 根据压缩数据的格式,横向展开数据列选取不同方式 crosstab函数 PIVOT 操作符 PIVOT 操作符的限制 工具 ksql 的元命令 \c ...

  2. Linux_more_less总结

    先写结论 : less is more,使用less 优于使用more more 和 less的区别 优于more不能后退,而less 就在其基础上增加了后退功能 less 可以使用键盘上的上下方向键 ...

  3. 《Win10——如何设置开机自启动项》

    Win10--如何设置开机自启动项       1. 为需要自启动的程序创建快捷方式. 2. Win+R输入"shell:startup",按下回车键出现一个文件夹. 3. 将快捷 ...

  4. 数论进阶&#160;

    数论进阶 扩展欧几里得算法 裴蜀定理(Bézout's identity) \(1\) :对于任意整数 \(a\),\(b\) ,存在一对整数 \(x\) ,\(y\) ,满足 \(ax+by=GCD ...

  5. Fluentd部署:系统配置

    Fluentd的全局配置项,诸如开启RPC.使用多worker模式等.可在配置文件中通过<system>进行配置,或通过命令行进行配置. 参数 workers:指定worker进程数,默认 ...

  6. 制造企业有可能自行开发ERP系统吗?

    当然可以啊! 生产企业对于ERP的需求是一直存在的,但市场上多为标准化的产品,与企业的个性化需求矛盾着. 有很多制造企业自行开发ERP系统啊!只是各个企业成效不同而已,毕竟不同企业的IT开发能力不同而 ...

  7. 工厂数字化转型离不开 MES 的原因是什么?

    工厂数字化转型是离不开 MES,首先得弄清楚什么是工厂数字化转型.什么是MES,它们的关系是怎样的. 数字化的主要含义是构建"业务数字化.数字资产化.资产服务化.服务业务化"闭环, ...

  8. SpringBoot课程学习(二)

    一.断言 (1).@assertTrue,@assertFalse assertTrue与assertFalse用来判断条件是否为true或false,assertTrue表示如果值为true则通过, ...

  9. 华为交换机GVRP基础配置

    GVRP基础配置 int G0/0/1 port link-type trunk 配置接口类型为trunk port trunk allow-pass vlan all 允许所有VLAN通过 int ...

  10. .NET Core C#系列之XiaoFeng.Threading.JobScheduler作业调度

    作业调度其实就是一个定时器,定时完成某件事, 比如:每分钟执行一次,每小时执行一次,每天执行一次,第二周几执行,每月几号几点执行,间隔多少个小时执行一次等. 作业类:XiaoFeng.Threadin ...