icpc2025南京vp记录
过题请况
\(CGK\)(赛时)\(FI\)(赛后)
回顾:
开局大家分头找签到,我们在发现 \(C\) 是签到之后我也是很顺利地签下了,然后就发现 \(F\) 居然已经有人过了,于是我就开始坐牢看 \(F\) (看到赛后都没过悲),坐牢期间抽空发现了 \(K\) 只要车没法一步获胜就无法获胜,然后牢函直接就把 \(YES\) 交成 \(TES\) 贡献了全场唯一一发罚时。然后我和伟大哥一起看来了一会 \(F\) ,我直接口胡一种错解开始写 \(F\) ,三个人坐了一会牢,伟大哥和牢函开始看 \(G\) ,看了一会伟大哥说他有思路了,上机写了一发直接就过了(膜拜伟大哥),看一一眼榜单发现已经铜了,就想要争取一下银,期间又去看来一眼 \(I\) 发现是不熟悉的期望 \(DP\) 直接就回去看 \(F\) 了,到最后也没做出来,遗憾止步于铜牌。
补题记录
\(F\)
题面:
给定一张 \(n\) 个节点的无向图,一开始图中没有边。维护 \(q\) 次操作,操作可能是以下两种:
● \(+\) \(u\) \(w\) :在节点 \(u\) 和之间连一条边,边权为 \(w\).
● \(?\) \(u\) \(w\) :定义路径的权值为,路径上所有边权进行按位与的最大值。
其中 \(n\le 1000\) , \(w\le 2^{12}\)
观察数据范围,我们不难发现 \(n\) 和 \(w\) 的范围都不大, \(q\) 个询问,复杂度不允许重跑图。这里我们引入一个 \(trick\),面对点和边都不大的情况下,由于这一题的路径长度是按位与这样的操作,我们可以利用并查集来将路径长度问题转化为并查集上的连通性问题。
回到本题,我们考虑构造 \(w\) (约4096)个并查集,在第 \(i\) 个并查集上的 \(u\) 和 \(v\) 联通意味着两点之间允许 \(w\) 的路径通过。现在我们来处理加边的操作,当引入一条边权为 \(w\) 的边,我们允许任何 \(w\) 的子集通过。
for(int M =w;;M=(M-1)&w) {
a[M].unite(u,v);
if(M==0)break;
}
利用上述代码可以对 \(w\) 的子集进行枚举,对于每一条边,我们都枚举子集,在子集上的并查集做联通。
针对询问操作,我们可以按位贪心操作,我们从最高位开始枚举,由于我们在加边操作里面枚举子集做并查集,高位能取就一定要取,能不能取我们通过在当前并查集上面判断连通性来判断。
int M=0;
for(int b=11;b>=0;b--){
if(a[M|(1<<b)].find(u)==a[M|(1<<b)].find(v)){
M|=(1<<b);
}
}
ans+=M;
\(I\)
题面:
Grammy 有两位队友小 A 和小 B。平时,两位队友每天都会出去吃好吃的餐厅,而 Grammy一般都吃学校食堂。因为学校食堂省时省钱,而出去吃饭有时候还要承担打车的费用。即使如此,有时Grammy 也会选择和队友们一起出去改善伙食。
马上就要月底了,Grammy 的生活费也只剩下 \(m\) 元,她需要用这些钱度过接下来的 \(n\) 天。在第 \(i\)天,她有两种选择:
- 在食堂吃饭,获得 \(a_i\) 的满意度,并花费 \(b_i\) 元。
- 和队友们出去吃,获得 \(c_i\) 的满意度,并花费 \(d_i\) 元。
由于小 A 和小 B 每天都出去吃,因此每天都会产生打车费用,其中第 \(i\) 天的打车费用为 \(e_i\)。三人为此制定了一个付打车费的规则:
-
若第 \(i\) 天 Grammy 没有出去吃,则本次打车费有 \(p_i / 100\) 的概率由小 A 支付,\((1 - p_i /100)\) 的概率由小 B 支付。
-
若第 \(i\) 天 Grammy 也出去吃了,且自 Grammy 上次支付打车费以来(若 Grammy
未支付过打车费,则从第一天算起),小 A 和小 B 都各自支付了至少一次打车费(无论当时 Grammy是否出去吃),则本次打车费由 Grammy 支付。否则按上一条规则,由一位队友支付。
Grammy 将在每天吃饭前选择今天的行动。她每天都能知道前一天打车费的支付情况,且目标是最大化总满意度的期望值,同时在最差情况下的总开销不能超过 \(m\)元。
问:如果她每一天的决策都是最优的,总满意度的期望值是多少。
不难看出这一题应该是期望 \(DP\),但是难点在于开销不能超过 \(m\) 的约束要怎么在 \(DP\) 中体现,于是我们参考背包问题,按 \(m\) 的从大到小来转移,就能保证开销的约束。
接着我们来设计状态,对一个状态的描述我们需要四个参数 \(p\),\(q\),\(a\),\(b\),表示第 \(p\) 天,剩余 \(q\) 元,同学 \(A\) 和同学 \(B\) 是否已经付过款了。
开始设计转移,转移存在两种情况:
- 在食堂吃饭,获得 \(a_i\) 的满意度,并花费 \(b_i\) 元。
此时 \(val1=a_i+p_i*dp[p+1][q-b_i][1][b]+(1-p_i)*dp[p+1][q-b_i][a][1]\)
- 和队友们出去吃,获得 \(c_i\) 的满意度,并花费 \(d_i\) 元。
当队友都付过钱即 \(a,b=1\) 时 \(val2=c_i+dp[p+1][q-d_i-e_i][0][0]\)
当有队友没付过钱时 \(val2=c_i+p_i*dp[p+1][q-d_i][1][b]+(1-p_i)*dp[p+1][q-d_i][a][1]\)
这样做 \(DP\) 即可
for(int i=n;i>=1;i--){
for(int j=0;j<=m;j++){
for(int A=0;A<=1;A++){
for(int B=0;B<=1;B++){
int q=j-b[i];
if(j>=b[i]&&(dp[i+1][q][1][B]>=0||p[i]==0)&&(dp[i+1][q][A][1]>=0||p[i]==1)){
double val1=a[i]+p[i]*dp[i+1][q][1][B]+(1-p[i])*dp[i+1][q][A][1];
dp[i][j][A][B]=max(dp[i][j][A][B],val1);
}
if(A==1&&B==1){
int q=j-d[i]-e[i];
if(q>=0&&dp[i+1][q][0][0]>=0){
double val2=c[i]+dp[i+1][q][0][0];
dp[i][j][A][B]=max(dp[i][j][A][B],val2);
}
}else{
int q=j-d[i];
if(q>=0&&(dp[i+1][q][1][B]>=0||p[i]==0)&&(dp[i+1][q][A][1]>=0||p[i]==1)){
double val2=c[i]+p[i]*dp[i+1][q][1][B]+(1-p[i])*dp[i+1][q][A][1];
dp[i][j][A][B]=max(dp[i][j][A][B],val2);
}
}
}
}
}
}

浙公网安备 33010602011771号