隐藏页面特效

Codeforces Round 889 (Div. 1)

1|0Preface


由于一轮集训最后一周题目难度变大加上要写专题补专题导致欠了很多的博客没写,接下来慢慢把它们补上吧

才不是因为天天溜会寝室看LPL呢,我发誓

顺序的话就倒着来好了,先从最后的这场收尾的CF补起好了

这场其实刚开始就被A1,A2卡的很难受,大概1h左右过了之后一直在刚B,其实核心的思路全部都想到了但就是没想到用bitset去优化那个东西(看到区间平移下意识地以为是平衡树)

不过惊喜的是这样都不掉分真是绷不住了,感觉靠刮痧真有机会刮上2100的说


2|0A1. Dual (Easy Version)&&A2. Dual (Hard Version)


这题其实说穿了很简单,但就是需要一点灵光想到刚开始的那步

首先考虑如果所有数都0,那么有一个显而易见的n1步的做法,就是从左往右每次把前一个数加到后一个数上

同样的如果所有数都0,就可以从右往左把后一个数加到前一个数上去

但现在有一个问题就是序列中的数有正有负该怎么处理,以把所有数变成0的情况为例

我们肯定是找到最大的那个正数,然后通过将其翻倍来让它比绝对值最大的那个负数的绝对值大,然后我们在把所哟的负数加上它即可

但只是这样的话不足以通过A2,其实仔细想一下这个东西就是个trade-off,我们每次判断下是把所有数改成0还是0更优即可

比赛的时候想到这个就感觉绝对是对的了,就没细算操作次数,最后样例一过直接一交就过了

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; const int N=25,INF=1e9; int t,n,a[N],mi,mx,cm,cp,cnt,x[100],y[100]; inline int calc(int x,int y) { if (!x) return INF; int cnt=0; while (x<y) ++cnt,x*=2; return cnt; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),cnt=mi=mx=cm=cp=0,i=1;i<=n;++i) { if (scanf("%d",&a[i]),a[i]>0) mx=max(mx,a[i]),++cp; if (a[i]<0) mi=max(mi,-a[i]),++cm; } if (!mi&&!mx) { puts("0"); continue; } if (calc(mx,mi)+cm<=calc(mi,mx)+cp) { int pos; for (i=1;i<=n;++i) if (a[i]==mx) { pos=i; break; } while (mx<mi) ++cnt,x[cnt]=y[cnt]=pos,mx*=2; for (i=1;i<=n;++i) if (a[i]<0) ++cnt,x[cnt]=i,y[cnt]=pos; for (i=2;i<=n;++i) ++cnt,x[cnt]=i,y[cnt]=i-1; } else { int pos; for (i=1;i<=n;++i) if (a[i]==-mi) { pos=i; break; } while (mi<mx) ++cnt,x[cnt]=y[cnt]=pos,mi*=2; for (i=1;i<=n;++i) if (a[i]>0) ++cnt,x[cnt]=i,y[cnt]=pos; for (i=n-1;i>=1;--i) ++cnt,x[cnt]=i,y[cnt]=i+1; } for (printf("%d\n",cnt),i=1;i<=cnt;++i) printf("%d %d\n",x[i],y[i]); } return 0; }

3|0B. Earn or Unlock


首先有一个很trivial的DP做法,设fi,j表示处理了前i张牌,解锁了前j张牌时能获得的最大收益

则转移很显然,不过要注意合法的状态必须满足ij

  • 用第i张牌去解锁,fi,jfi+1,j+ai
  • 获得第i张牌的价值,fi,jfi+1,j+ai

然后观察DP的式子我们会发现其实已知i,j的情况下贡献是确定的,因为只要能确定能到达这个状态了那么操作的顺序啥的就无关紧要了

换句话说,假如最后解锁前x张牌的状态是合法的,那么它对应的贡献一定是a1+a2axx+1

那么我们的DP其实只要考虑第一种情况即可,这种状态的平移直接用bitset就很好解决了(因为我们只要知道每个位置的可达性即可)

实现的时候可以设第n+12n的牌都是0,总复杂度O(n2ω)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #define int long long #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; const int N=200005; int n,a[N],pfx[N],ans; bitset <N> f; signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]),pfx[i]=pfx[i-1]+a[i]; for (f.set(1),i=1;i<=n;++i) { f|=f<<a[i]; if (f.test(i)) ans=max(ans,pfx[i]-i+1),f.reset(i); } for (i=n+1;i<=2*n;++i) if (f.test(i)) ans=max(ans,pfx[n]-i+1); return printf("%lld",ans),0; }

4|0C. Expected Destruction


比赛的时候看到期望就不敢做了,其实赛后一看感觉海星,就当学个思路吧

首先把问题转化为,在长为m+1的数轴上有若干个棋子,每次可以选一个棋子向后走一步,同一个位置最多存在一个棋子,当所有棋子到达m+1时结束

那么如果没有任何两个棋子在同个地方相遇的话答案就是i=1n(mai+1),现在我们考虑减去由于位置重合带来的影响

如果在位置x有两个棋子相遇,那么它们带来的贡献减少是显然的,就是mx+1,问题就是怎么求出概率

如果直接用后面的棋子追上前面的棋子后两个合并成一个的这种观点的话就很难想清楚,其实我们仔细想一下可以发现不妨假设后面的棋子在追上前面的棋子后就直接消失了

这样有一个好处就是所有的棋子间的相遇都只可能在相邻的棋子间进行,而根据期望的线性性,这样处理是没问题的

那么我们不妨单独考虑两个相邻的棋子,只考虑它们的移动时就是个经典套路,就是各自12的概率向前走

fx,y表示两个棋子分别处于位置x,y时减去的期望贡献,转移非常trivial,只是要注意当y=m+1时要直接中止返回0

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long u64; typedef pair <int,int> pi; typedef vector <int> VI; const int N=505,mod=1e9+7,inv2=(mod+1)/2; int n,m,a[N],ret,f[N][N]; inline int DP(CI x,CI y) { if (x==y) return m-x+1; if (y==m+1) return 0; if (~f[x][y]) return f[x][y]; return f[x][y]=(1LL*DP(x+1,y)*inv2%mod+1LL*DP(x,y+1)*inv2%mod)%mod; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&a[i]),(ret+=m-a[i]+1)%=mod; for (memset(f,-1,sizeof(f)),i=1;i<n;++i) (ret+=mod-DP(a[i],a[i+1]))%=mod; return printf("%d",ret),0; }

5|0Postscript


补天咯


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17594626.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
历史上的今天:
2020-07-31 AtCoder Grand Contest 033
点击右上角即可分享
微信分享提示