暑假集训六 [接力比赛,树上竞技,虚构推理,记忆碎片]

暑假集训六

别问为什么从六开始。

题面

A.接力比赛

两个01背包跑一遍。
别问代码为啥写的这么阴间。

image

Code

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1010, MAXV = 1e6 + 10;
const long long INF = 1e18 + 1145141919810;
int n, m;
long long w, v, ans;
long long sum_w[MAXN], sum_b[MAXN];
long long dp[2][MAXV];
bool vis_w[MAXV], vis_b[MAXV];
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
n = read(), m = read();
vis_w[0] = true;
for(register int i = 1; i <= n; i++){
w = read(), v = read();
sum_w[i] = sum_w[i - 1] + w;
for(register int j = 1; j <= w; j++)
dp[0][j + sum_w[i - 1]] = -INF;
for(register int j = sum_w[i - 1]; j >= 0; j--){
if(vis_w[j]){
dp[0][j + w] = max(dp[0][j + w], dp[0][j] + v);
vis_w[j + w] = true;
}
}
}
vis_b[0] = true;
for(register int i = 1; i <= m; i++){
w = read(), v = read();
sum_b[i] = sum_b[i - 1] + w;
for(register int j = 1; j <= w; j++)
dp[1][j + sum_b[i - 1]] = -INF;
for(register int j = sum_b[i - 1]; j >= 0; j--){
if(vis_b[j]){
dp[1][j + w] = max(dp[1][j + w], dp[1][j] + v);
vis_b[j + w] = true;
}
}
}
long long len = max(sum_w[n], sum_b[m]);
for(register int i = 1; i <= len; i++){
if(vis_w[i] && vis_b[i])
ans = max(ans, dp[0][i] + dp[1][i]);
}
printf("%lld", ans);
return 0;
}

B.树上竞技

image
image

按照边去考虑,如果在一条边 E 的两侧分别有 xmx 个关键点,

不妨令 xmx 那么最后的汇聚点一定在 mx 这个方向,

这样可以只让 x 条边经过 E,代价较小

然后设某条边两侧分别共有 sns 个点,容易发现我们需要求

i=1m1min{i,mi}×CsiCnsmi

发现 min 不是很好处理,考虑把它拆开来,还要加上 m 为偶数的情况,用 DFS 预处理节点子树大小,原式可以化成

ans=u=2ni=1m12i×Csize[u]iCnsize[u]mi+i×Csize[u]miCnsize[u]i+[m%2==0]m2×Csm2Cnsm2

后面的一小点式子可以暴力硬扫,考虑前面的怎样优化

不妨设 k=m2,发现比较显然地,

i=1ki×CsiCnsmi=si=1kCs1i1Cnsmi

不妨令 f(s)=i=1kCs1i1Cnsmi,那么答案就是

u=2nf(size[u])+f(nsize[u])

考虑如何求出 f(s),发现它的组合意义是 n1 个物品里选择了 m1 个,前面 s1 个物品中选择了 k1 个的方案数

如何递推 f(s)

不难发现,f(s) 变成 f(s+1) 变少的部分就是前 s1 个点中选择 k1 个,选择了 s 为第 k 个点,后面 ns1 个点里选择了 mk1 个点

这是为什么呢?我们考虑如果没有选择 s 作为第 k 个点

那么前 s 个点中选择 k1 个点和前 s1 个点中选择 k1 个是等价的

所以必须让 s 作为第 k 个点被选择,多出来的方案数根据乘法原理就是

Cs1k1Cns1mk1

也就是说

f[s+1]=f[s]+Cs1k1Cns1mk1

统计答案即可。

算是偷的markdown。

Code

#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int Mod = 1e9 + 7;
const int MAXN = 1e6 + 10;
int n, m, k, cnt;
LL ans;
int head[MAXN], size[MAXN];
LL fac[MAXN], inv[MAXN];
LL f[MAXN];
struct Edge{
int to, next;
}e[MAXN << 1];
inline void Add(int u, int v){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
LL Fast_Pow(LL a, LL w, LL p){
LL ans = 1;
while(w){
if(w & 1) ans = ans * a % p;
a = a * a % p;
w >>= 1;
}
return ans;
}
LL Inv(LL a, LL p){
return Fast_Pow(a, p - 2, p);
}
LL C(LL n, LL m, LL p){
if(!m) return 1;
if(m > n) return 0;
return fac[n] * inv[m] % p * inv[n - m] % p;
}
void init(){
fac[1] = 1;
for(register int i = 2; i <= n; i++)
fac[i] = 1LL * fac[i - 1] * i % Mod;
inv[n] = Inv(fac[n], Mod);
for(register int i = n - 1; i >= 0; i--)
inv[i] = 1LL * inv[i + 1] * (i + 1) % Mod;
}
void dfs(int rt, int fa){
size[rt] = 1;
for(register int i = head[rt]; i; i = e[i].next){
int v = e[i].to;
if(v == fa) continue;
dfs(v, rt);
size[rt] += size[v];
ans = 1LL * (ans + f[size[v]] % Mod + f[n - size[v]] % Mod) % Mod;
if(!(m & 1)) ans = 1LL * (ans + C(size[v], m / 2, Mod) * C(n - size[v], m / 2, Mod) % Mod * (m / 2) % Mod) % Mod;
}
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int main(){
freopen("meeting.in", "r", stdin);
freopen("meeting.out", "w", stdout);
n = read(), m = read();
init();
for(register int i = 1; i <= n - 1; i++){
int u;
u = read();
Add(u, i + 1);
Add(i + 1, u);
}
k = (m - 1) / 2;
if(k >= 1) f[1] = C(n - 1, m - 1, Mod);
for(register int i = 2; i <= n; i++)
f[i] = 1LL * ((f[i - 1] - C(i - 2, k - 1, Mod) * C(n - i, m - k - 1, Mod) % Mod) % Mod + Mod) % Mod;
for(register int i = 2; i <= n; i++)
f[i] = 1LL * f[i] * i % Mod;
dfs(1, 0);
printf("%lld", ans);
return 0;
}

C.虚构推理

image
image

其实不用题解那么麻烦,可以枚举时间再lower_bound优化查询。

查询到和当前时间的角最大的时间就可以。

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 5e4 + 10;
const long double EPS = 1e-10;
int n;
long double ans = 1e10;
long double angle_h[MAXN], angle_m[MAXN];
struct Clock{
int h, m, s;
}c[MAXN];
inline long double fmax(long double x, long double y){
return (x - y > EPS) ? x : y;
}
inline long double fmin(long double x, long double y){
return (y - x > EPS) ? x : y;
}
inline long double fabs(long double x){
return (0 - x > EPS) ? -x : x;
}
long double Get_Cut(long double angle1, long double angle2){
return fmin(fabs(angle1 - angle2), 360.0 - fabs(angle1 - angle2));
}
long double Find_Cut(long double angle, long double *clock){
int pos1, pos2;
if(180.0 - angle > EPS){
pos1 = lower_bound(clock + 1, clock + 1 + n, angle + 180.0) - clock;
pos2 = pos1 - 1;
}
else{
pos1 = lower_bound(clock + 1, clock + 1 + n, angle - 180.0) - clock;
pos2 = pos1 - 1;
}
if(pos1 == n + 1) pos1 = 1;
if(pos2 == 0) pos2 = n;
return fmax(Get_Cut(angle, clock[pos1]), Get_Cut(angle, clock[pos2]));
}
int main(){
freopen("unreal.in", "r", stdin);
freopen("unreal.out", "w", stdout);
scanf("%d", &n);
for(register int i = 1; i <= n; i++){
scanf("%d:%d:%d", &c[i].h, &c[i].m, &c[i].s);
if(c[i].h >= 12) c[i].h -= 12;
angle_m[i] = 1.0 * c[i].m * 6.0 + c[i].s * 0.1;
angle_h[i] = 1.0 * c[i].h * 30.0 + c[i].m * 0.5 + c[i].s / 120.0;
}
sort(angle_m + 1, angle_m + 1 + n);
sort(angle_h + 1, angle_h + 1 + n);
for(register long double h = 0; h < 12; h++){
for(register long double m = 0; m < 60; m++){
for(register long double s = 0; s < 60; s += 0.1){
long double m_angle = 1.0 * m * 6.0 + s * 0.1;
long double h_angle = 1.0 * h * 30.0 + m * 0.5 + s / 120.0;
ans = fmin(ans, fmax(Find_Cut(m_angle, angle_m), Find_Cut(h_angle, angle_h)));
}
}
}
printf("%Lf", ans);
return 0;
}

D.记忆碎片

image
image

不会,可以去看Delov 大佬的题解
或者SoyTony大佬的题解

posted @   TSTYFST  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示