隐藏页面特效

Codeforces Round 864 (Div. 2)

1|0Preface


周六的最后一战,感觉脑子确实有点混沌了一个C题好多细节没考虑好心态差点爆炸

最后还好20min速切了D稳了手排名,不然已经回到蓝名了

感觉最近打比赛老是犯一些很离谱的失误,题目老是想不清楚导致好几场没法在比赛时写掉Div2的E了

嘛说白了还是练得少了,多刷题解决一切问题的说


2|0A. Li Hua and Maze


不难发现如果这两个点中有一个在角落上,那么只要堵住它临近的两个格子即可

否则如果有一个在边上,那么只要堵住它临近的三个格子即可

否则我们肯定可以用四个格子堵住其中任意一个

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int t,n,m,X1,Y1,X2,Y2; inline bool corner(CI x,CI y) { return (x==1&&y==1)||(x==1&&y==m)||(x==n&&y==1)||(x==n&&y==m); } inline bool edge(CI x,CI y) { return x==1||x==n||y==1||y==m; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { scanf("%d%d%d%d%d%d",&n,&m,&X1,&Y1,&X2,&Y2); if (corner(X1,Y1)||corner(X2,Y2)) puts("2"); else if (edge(X1,Y1)||edge(X2,Y2)) puts("3"); else puts("4"); } return 0; }

3|0B. Li Hua and Pattern


首先把所有翻转后对应的位置上的颜色比较一下,如果不一样就至少要花费一次操作

然后都执行完后看下如果k<0则显然无解,否则要满足2|k才能通过修改某对位置来满足要求

但是还要注意一个情况,当n为奇数时k为奇数也是合法的,因为我们可以把多余的操作全部扔在最中间的那个点上,刚开始没考虑到还WA了一发

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=1005; int t,n,k,a[N][N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) for (j=1;j<=n;++j) scanf("%d",&a[i][j]); for (i=1;i<=n;++i) for (j=1;j<=n;++j) if (a[i][j]!=a[n-i+1][n-j+1]) a[i][j]^=1,--k; if (k<0) { puts("NO"); continue; } if (k%2==0) { puts("YES"); continue; } if (n&1) puts("YES"); else puts("NO"); } return 0; }

4|0C. Li Hua and Chess


思路很简单,但是我就是猪脑过载想不清楚,前前后后卡了快1h真是菜得飞起

一个很naive的想法就是我们尽量询问角落上的点,这样每次询问就能确定目标点在两条线段上

通过连续的两次询问可以很容易地把答案锁定在某个线段上,然后这样在第三次询问时我们可以询问线段的一个端点来确定位置了

说起来轻巧但是要注意很多Corner Cases,比如考虑n,m的大小关系,相交的形态等等,具体看代码吧

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int t,n,m; inline int ask(CI x,CI y) { printf("? %d %d\n",x,y); fflush(stdout); int z; scanf("%d",&z); return z; } inline void answer(CI x,CI y) { printf("! %d %d\n",x,y); fflush(stdout); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { scanf("%d%d",&n,&m); int A,B,C; if (n>m) { A=ask(1,1); B=ask(n,1); if (1+A!=n-B) { if (A<B) answer(n-B,1+A); else if (A>B) answer(1+A,1+B); else { C=ask(n-B,1+A); answer(n-B+C,1+A); } } else { C=ask(1+A,1); answer(1+A,1+C); } } else { A=ask(1,1); B=ask(1,m); if (1+A!=m-B) { if (A<B) answer(1+A,m-B); else if (A>B) answer(1+B,1+A); else { C=ask(1+A,m-B); answer(1+A,m-B+C); } } else { C=ask(1,1+A); answer(1+C,1+A); } } } return 0; }

5|0D. Li Hua and Tree


早知道先写D题了,就是个模拟+STL的裸题

首先我们很容易想到旋转操作只会影响当前点、当前点的重儿子、当前点的父亲这三个点的信息

很显然可以直接用set维护下每个点的重儿子信息,然后手玩一下转移过程别搞错细节就行

#include<cstdio> #include<iostream> #include<set> #include<vector> #define int long long #define RI register int #define CI const int& using namespace std; const int N=100005; struct Data { int s,id; inline Data(CI S=0,CI ID=0) { s=S; id=ID; } friend inline bool operator < (const Data& A,const Data& B) { return A.s!=B.s?A.s>B.s:A.id<B.id; } }; multiset <Data> hp[N]; int n,m,a[N],sz[N],sum[N],fa[N],x,y; vector <int> v[N]; inline void DFS(CI now=1,CI f=0) { fa[now]=f; sum[now]=a[now]; sz[now]=1; for (int to:v[now]) if (to!=f) { DFS(to,now); sum[now]+=sum[to]; sz[now]+=sz[to]; hp[now].insert(Data(sz[to],to)); } } signed main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%lld%lld",&n,&m),i=1;i<=n;++i) scanf("%lld",&a[i]); for (i=1;i<n;++i) scanf("%lld%lld",&x,&y),v[x].push_back(y),v[y].push_back(x); for (DFS(),i=1;i<=m;++i) { if (scanf("%lld%lld",&x,&y),x==1) printf("%lld\n",sum[y]); else { if (hp[y].empty()) continue; int son=hp[y].begin()->id; hp[y].erase(hp[y].begin()); hp[fa[y]].erase(hp[fa[y]].find(Data(sz[y],y))); hp[son].insert(Data(sz[y]-sz[son],y)); hp[fa[y]].insert(Data(sz[y],son)); int tf=fa[y]; fa[y]=son; fa[son]=tf; int ts=sz[y]; sz[y]=ts-sz[son]; sz[son]=ts; int A=sum[y],B=sum[son]; sum[y]=A-B; sum[son]=A; } } return 0; }

6|0E. Li Hua and Array


什么大缝合题,数论+树论+数据结构大综合了属于是,不过每个涉及得都很浅所以还是很好想的

首先很自然可以想到我们对于每个数x(x>1),连一条ϕ(x)x的边,这样就形成了一颗树

然后询问就可以转化为求一个区间内所有点到它们的公共LCA的距离之和

很容易想到我们求出每个点的深度后就转化为区间求和和区间求LCA的问题了,但区间所有点的LCA乍一看感觉有点难处理的说

其实方法也很简单,我们找出区间内DFS序最小和最大的点,则它们的LCA就是区间内所有点的LCA,这个画个图看一下还是很好理解的说

然后考虑修改,这个就是经典套路了,由于取ϕlog次后就会变成1,所以我们可以直接暴力搞,势能分析一下复杂度是对的

当然这个性质还给我们带来一个好处,由于树高是log级别的,所以我们不需要在写求LCA的算法了,直接暴力跳即可

总复杂度O(nlogAi)

#include<cstdio> #include<iostream> #include<set> #define RI register int #define CI const int& using namespace std; const int N=100005,M=5000000; int n,m,a[N],tp,l,r,pri[M+5],cnt,phi[M+5],seq[M+5],dfn[M+5],dep[M+5],idx; bool vis[M+5]; set <int> s[M+5]; inline void init(CI n) { phi[1]=1; for (RI i=2,j;i<=n;++i) { if (!vis[i]) pri[++cnt]=i,phi[i]=i-1; for (j=1;j<=cnt&&i*pri[j]<=n;++j) if (vis[i*pri[j]]=1,i%pri[j]) phi[i*pri[j]]=phi[i]*(pri[j]-1); else { phi[i*pri[j]]=phi[i]*pri[j]; break; } } } inline void DFS(CI now=1) { seq[dfn[now]=++idx]=now; dep[now]=dep[phi[now]]+1; for (int to:s[now]) DFS(to); } inline int getLCA(int x,int y) { if (dep[x]<dep[y]) swap(x,y); while (dep[x]>dep[y]) x=phi[x]; if (x==y) return x; while (phi[x]!=phi[y]) x=phi[x],y=phi[y]; return phi[x]; } class Segment_Tree { private: struct segment { long long sum; int mi,mx; inline segment(const long long SUM=0,CI MI=1e9,CI MX=0) { sum=SUM; mi=MI; mx=MX; } }node[N<<2]; #define S(now) node[now].sum #define MI(now) node[now].mi #define MX(now) node[now].mx #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) { S(now)=S(now<<1)+S(now<<1|1); MI(now)=min(MI(now<<1),MI(now<<1|1)); MX(now)=max(MX(now<<1),MX(now<<1|1)); } public: inline void build(TN) { if (l==r) return (void)(node[now]=(segment){dep[a[l]],dfn[a[l]],dfn[a[l]]}); int mid=l+r>>1; build(LS); build(RS); pushup(now); } inline void modify(CI beg,CI end,TN) { if (beg<=l&&r<=end) { if (MX(now)==1) return; if (l==r) return (void)(a[l]=phi[a[l]],node[now]=(segment){dep[a[l]],dfn[a[l]],dfn[a[l]]}); } int mid=l+r>>1; if (beg<=mid) modify(beg,end,LS); if (end>mid) modify(beg,end,RS); pushup(now); } inline long long getsum(CI beg,CI end,TN) { if (beg<=l&&r<=end) return S(now); int mid=l+r>>1; long long ret=0; if (beg<=mid) ret+=getsum(beg,end,LS); if (end>mid) ret+=getsum(beg,end,RS); return ret; } inline int getmin(CI beg,CI end,TN) { if (beg<=l&&r<=end) return MI(now); int mid=l+r>>1,ret=1e9; if (beg<=mid) ret=min(ret,getmin(beg,end,LS)); if (end>mid) ret=min(ret,getmin(beg,end,RS)); return ret; } inline int getmax(CI beg,CI end,TN) { if (beg<=l&&r<=end) return MX(now); int mid=l+r>>1,ret=0; if (beg<=mid) ret=max(ret,getmax(beg,end,LS)); if (end>mid) ret=max(ret,getmax(beg,end,RS)); return ret; } #undef S #undef MI #undef MX #undef TN #undef LS #undef RS }SEG; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d%d",&n,&m),init(M),i=1;i<=n;++i) { scanf("%d",&a[i]); int tmp=a[i]; while (tmp>1) s[phi[tmp]].insert(tmp),tmp=phi[tmp]; } for (DFS(),SEG.build(),i=1;i<=m;++i) if (scanf("%d%d%d",&tp,&l,&r),tp==1) SEG.modify(l,r); else { int lca=getLCA(seq[SEG.getmin(l,r)],seq[SEG.getmax(l,r)]); printf("%lld\n",SEG.getsum(l,r)-1LL*dep[lca]*(r-l+1)); } return 0; }

7|0Postscript


这场比赛是国人出的吧都是Li Hua的说,自从高中毕业后都以为再也不会见到了结果在CF上见到了


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17310102.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
历史上的今天:
2018-04-12 Educational Codeforces Round 41 (Rated for Div. 2)(A~D)
点击右上角即可分享
微信分享提示