隐藏页面特效

Codeforces Round #848 (Div. 2)(持续更新)

1|0Preface


这场打的可以说发挥了水平但没有完全发挥的说

ABC都是一眼一遍过,结果D是我最怕的期望题,大概推了个DP的式子但感觉成环了初始条件又不够(flag)就弃了

本来当时看E只有十几个人过的,抱着随便看看的心态看了眼题面发现是个套路题

结果30minRush完代码后一直连着T了好多发,直到比赛结束前两分钟才卡常卡过去

嘛不过不管怎么说应该不会掉分的说(最近两场的分怎么被CF给吃掉了,还没更新上去就难绷)


2|0A. Flip Flop Sum


SB题,直接按题意枚举即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=100005; int t,n,a[N],pfx[N],suf[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]); for (pfx[0]=0,i=1;i<=n;++i) pfx[i]=pfx[i-1]+a[i]; for (suf[n+1]=0,i=n;i>=1;--i) suf[i]=suf[i+1]+a[i]; int ans=-1e9; for (i=1;i<n;++i) ans=max(ans,pfx[i-1]+suf[i+2]-a[i]-a[i+1]); printf("%d\n",ans); } return 0; }

3|0B. The Forbidden Permutation


SB题,话说这题题面写的很难懂赛后被好多人喷来着

不难发现由于只要有一个位置不合法即可,我们先特判掉答案为0的情况

然后考虑直接枚举每个位置,分别考虑破坏不等式两边所需要的最小步数即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=100005,INF=1e9; int t,n,m,d,p[N],pos[N],a[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%d%d",&n,&m,&d),i=1;i<=n;++i) scanf("%d",&p[i]),pos[p[i]]=i; for (i=1;i<=m;++i) scanf("%d",&a[i]); bool flag=0; int ans=INF; for (i=1;i<m;++i) { int x=pos[a[i]],y=pos[a[i+1]]; if (y<x||y>x+d) { flag=1; break; } ans=min(ans,y-x); if (n-1>d) ans=min(ans,d+1-(y-x)); } if (flag) puts("0"); else printf("%d\n",ans); } return 0; }

4|0C. Flexible String


爆搜题

不难发现关于答案的计算方式,我们只要考虑找出每段连续的两个串对应位置相同的区间长度即可

然后注意到题目中反复提到的字符集大小小于等于10的限制,可以直接爆搜改了哪些字符然后check即可

复杂度O(210×n),实际情况前面是C10k只会更小

#include<cstdio> #include<iostream> #include<vector> #define RI register int #define CI const int& using namespace std; const int N=100005; int t,n,k,tot,rst[26]; char a[N],b[N]; bool vis[N],cs[15]; vector <int> pos[15]; long long ans; inline long long C2(CI x) { return 1LL*x*(x+1)/2LL; } inline void DFS(CI now=1,CI num=0) { if (num>k) return; if (now==tot+1) { RI i; for (i=1;i<=n;++i) vis[i]=a[i]==b[i]; for (i=1;i<=tot;++i) if (cs[i]) for (int x:pos[i]) vis[x]=1; long long ret=0; int lst=0; for (i=1;i<=n;++i) if (!vis[i]) ret+=C2(i-1-lst),lst=i; ret+=C2(n-lst); ans=max(ans,ret); return; } cs[now]=1; DFS(now+1,num+1); cs[now]=0; DFS(now+1,num); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (tot=0,i=0;i<26;++i) rst[i]=0; for (i=0;i<15;++i) pos[i].clear(); for (scanf("%d%d%s%s",&n,&k,a+1,b+1),i=1;i<=n;++i) { if (!rst[a[i]-'a']) rst[a[i]-'a']=++tot; pos[rst[a[i]-'a']].push_back(i); } ans=0; DFS(); printf("%lld\n",ans); } return 0; }

5|0D. Flexible String Revisit


DP题,方程很好写但是求解需要技巧

不难发现我们只关心两个串不同的位置的个数时多少,因此直接设fi表示有i个不同位置的期望步数

转移方程显然有:fi=fi+1×nin+fi1×in+1

即我们对当前状态进行操作,有in的概率选中一个不同的位,这样还要操作fi1次,反之则有nin的概率还要操作fi+1

然后这个DP看似是成环的,当时我只发现了f0=0这一个终止条件,但没有注意到fn=fn1+1这个特殊情况

有了这个边界之后我们就可以考虑解方程了,因为那个转移的式子可以写成:

fi1=fi×ni+1n+fi2×i1n+1fi=(fi11fi2×i1n)×nni+1

这是一个关于前两项的递推式,而根据我们的两个已知条件式可以解出来的

我们不妨设f1是已知的初始条件,最后我们可以把每个fi都用一个二元组(x,y)来表示,其中fi=x×f1+y

这样我们在得到了fn,fn1的表示后可以反解出f1的值,从而推出其中的任意一项了

不得不说这个trick之前没有见过,算是长见识了的说

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=1000005,mod=998244353; inline int sum(CI x,CI y) { return x+y>=mod?x+y-mod:x+y; } inline int sub(CI x,CI y) { return x-y<0?x-y+mod:x-y; } inline int quick_pow(int x,int p=mod-2,int mul=1) { for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul; } struct Data // x*f[1]+y { int x,y; inline Data(CI X=0,CI Y=0) { x=X; y=Y; } friend inline Data operator + (const Data& A,const Data& B) { return Data(sum(A.x,B.x),sum(A.y,B.y)); } friend inline Data operator - (const Data& A,const Data& B) { return Data(sub(A.x,B.x),sub(A.y,B.y)); } friend inline Data operator * (const Data& A,const Data& B) { return Data(1LL*A.x*B.x%mod,1LL*A.y*B.y%mod); } }f[N]; int t,n,invn,dif; char a[N],b[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%s%s",&n,a+1,b+1),dif=0,i=1;i<=n;++i) dif+=a[i]!=b[i]; for (f[0]=Data(0,0),f[1]=Data(1,0),invn=quick_pow(n),i=2;i<=n;++i) { int tmp=1LL*(i-1)*invn%mod,div=quick_pow(sub(1,tmp)); f[i]=(f[i-1]-Data(0,1)-(f[i-2]*Data(tmp,tmp)))*Data(div,div); } Data tmp=f[n]-f[n-1]; int f1=1LL*sub(1,tmp.y)*quick_pow(tmp.x)%mod; printf("%d\n",sum(1LL*f[dif].x*f1%mod,f[dif].y)); } return 0; }

6|0E. The Tree Has Fallen!


套路题

一堆数里选一些出来求最大的异或和,这个一眼线性基没跑了

换根操作的话一眼先定根然后再分类讨论,顺着这个思路这题就很简单了

首先假定1为根,然后我们考虑在这个有根树上把根换成r对于v的影响:

  • r不在v的子树中时,不难发现此时我们要找答案的数集就还是v的子树
  • rv的子树中时,手玩一下我们发现,设wv的直接儿子,并且满足rw的子树中,则此时我们要找答案的数集就是所有数的集合减去w的子树

因此我们把数的DFS序求出来,这样就变成了区间询问的题目了,由于线性基支持合并,因此直接上线段树即可

但是由于O(nlog2n)的复杂度且由于我各部分的封装实现,常数巨大,卡了好久的常数才跑过

后来比赛结束后躺在床上转念一想由于只有查询操作,且两个区间合并时有重复不会影响答案(因为重复的部分相当于插入了两次线性基,对答案没有影响),因此可以用ST表来写

不过由于我太懒的说既然卡过了就万事大吉了,就懒得再写了

#include<cstdio> #include<iostream> #include<vector> #include<cstring> #define RI register int #define CI const int& using namespace std; const int N=200005,P=30; int t,n,a[N],x,y,q,dep[N],dfn[N],L[N],R[N],tot; vector <int> v[N]; struct LB { int v[P]; inline LB(void) { memset(v,0,sizeof(v)); } inline void clear(void) { memset(v,0,sizeof(v)); } inline void insert(int x) { for (RI i=P-1;~i;--i) if ((x>>i)&1) { if (!v[i]) return (void)(v[i]=x); x^=v[i]; } } inline int query(int x=0) { for (RI i=P-1;~i;--i) if ((x^v[i])>x) x^=v[i]; return x; } friend inline LB operator + (const LB& A,const LB& B) { LB C; RI i; for (i=0;i<P;++i) C.v[i]=A.v[i]; for (i=0;i<P;++i) if (B.v[i]) C.insert(B.v[i]); return C; } }; class Segment_Tree { private: LB tr[N<<2]; #define TN CI now=1,CI l=1,CI r=n #define LS now<<1,l,mid #define RS now<<1|1,mid+1,r inline void pushup(CI now) { tr[now]=tr[now<<1]+tr[now<<1|1]; } public: inline void build(TN) { if (l==r) return tr[now].clear(),tr[now].insert(a[dfn[l]]); int mid=l+r>>1; build(LS); build(RS); pushup(now); } inline LB query(CI beg,CI end,TN) { if (beg>end) return LB(); if (beg<=l&&r<=end) return tr[now]; int mid=l+r>>1; if (beg<=mid&&end<=mid) return query(beg,end,LS); if (end>mid&&beg>mid) return query(beg,end,RS); return query(beg,end,LS)+query(beg,end,RS); } #undef TN #undef LS #undef RS }SEG; class Tree_Solver { private: int anc[N][20]; public: inline void DFS(CI now=1,CI fa=0) { RI i; for (i=0;i<20;++i) anc[now][i]=0; dep[now]=dep[anc[now][0]=fa]+1; dfn[++tot]=now; L[now]=tot; for (i=0;i<19;++i) if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break; for (int to:v[now]) if (to!=fa) DFS(to,now); R[now]=tot; } inline void jump(int& x,CI y) { for (RI i=20;~i;--i) if ((y>>i)&1) x=anc[x][i]; } }T; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),v[i].clear(); for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x); for (tot=0,T.DFS(),SEG.build(),scanf("%d",&q),i=1;i<=q;++i) { if (scanf("%d%d",&x,&y),x==y) printf("%d\n",SEG.query(1,n).query()); else { if (L[y]<=L[x]&&L[x]<=R[y]) { T.jump(x,dep[x]-dep[y]-1); printf("%d\n",(SEG.query(1,L[x]-1)+SEG.query(R[x]+1,n)).query()); } else printf("%d\n",SEG.query(L[y],R[y]).query()); } } } return 0; }

__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17087534.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(140)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
历史上的今天:
2020-02-02 BZOJ 3622: 已经没有什么好害怕的了
2020-02-02 BZOJ 2553: [BeiJing2011]禁忌
2020-02-02 BZOJ 4767: 两双手
2020-02-02 BZOJ 3601: 一个人的数论
点击右上角即可分享
微信分享提示