隐藏页面特效

Educational Codeforces Round 146 (Rated for Div. 2)

1|0Preface


补题ing

值得一提的时补这场的时候先是遇上了CF的12小时大维护,后面又遇到了评测机崩了测不了也是有点有意思的说


2|0A. Coins


傻逼题,首先考虑2|n时一定有解x=n2,y=0,否则若2n2|k则由裴蜀定理知此时一定无解

否则y必为奇数,我们令x=nk2,y=1,则只要检验n,k的大小关系即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int t; long long k,n; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { if (scanf("%lld%lld",&n,&k),n%2==0) { puts("YES"); continue; } if (k%2==0) puts("NO"); else puts(n>=k?"YES":"NO"); } return 0; }

3|0B. Long Legs


一眼题,首先考虑枚举最后m的值x,则贪心地肯定把步数长的跳放在最后

因此容易列出当前答案的式子其实就是x1+ax+bx,只要求其最小值即可

考虑到如果我们不考虑上取整,把定义域改成实数域,则这个式子就是个典型的对勾函数,在x=ab时取得最小值

因此我们知道当枚举的x到这个级别的时候就差不多能看出答案了,这里我结合数据组数选择枚举到105即可通过

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

4|0C. Search in Parallel


傻逼贪心题,首先给ri从大到小排序后,先处理掉大的一定最优,然后放在哪里显然也就是比较一下选代价小的那个

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=200005; struct Data { int r,id; friend inline bool operator < (const Data& A,const Data& B) { return A.r>B.r; } }a[N]; int t,n,s1,s2,A[N],B[N],cnt1,cnt2; 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,&s1,&s2),i=1;i<=n;++i) scanf("%d",&a[i].r),a[i].id=i; sort(a+1,a+n+1); for (cnt1=cnt2=0,i=1;i<=n;++i) if ((cnt1+1)*s1<(cnt2+1)*s2) A[++cnt1]=a[i].id; else B[++cnt2]=a[i].id; for (printf("%d ",cnt1),i=1;i<=cnt1;++i) printf("%d ",A[i]); putchar('\n'); for (printf("%d ",cnt2),i=1;i<=cnt2;++i) printf("%d ",B[i]); putchar('\n'); } return 0; }

5|0D. Balancing Weapons


刚开始想了个算法码了100多行假了,后来发现是我想复杂了

首先我们肯定可以让最后每个数变成lcmfi,因此答案为n的情况是一定可得的

否则我们考虑至少存在一个点不会改变,我们枚举不改变的点pi,不难发现此时其它点要变一定会变成距离pi最近的那两个数

因此换句话说此时所有取值的可能其实是O(n)级别的,我们把所有这些可能的取值排个序,然后two pointers找出所有极大的合法区间并验证即可

具体实现可以看代码,总复杂度O(n2logn)

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=3005; struct Data { long long v; int tp,id; inline Data(const long long& V=0,CI TP=0,CI ID=0) { v=V; tp=TP; id=ID; } friend inline bool operator < (const Data& A,const Data& B) { return A.v<B.v; } }rst[N*3]; int t,n,k,ans,cnt,f[N],d[N],bkt[N]; long long p[N]; inline int solve(const long long& tar) { RI i,j; int tot=0,sum=0,ret=n; auto add=[&](const Data& it) { if (sum+=it.tp,++bkt[it.id]==1) ++tot; }; auto del=[&](const Data& it) { if (sum-=it.tp,--bkt[it.id]==0) --tot; }; for (cnt=0,i=1;i<=n;++i) { bkt[i]=0; rst[++cnt]=Data(p[i],1,i); long long tmp=tar/f[i]*f[i]; if (tmp>0) rst[++cnt]=Data(tmp,0,i); rst[++cnt]=Data(tmp+f[i],0,i); } for (sort(rst+1,rst+cnt+1),i=j=1;i<=cnt;++i) { while (j<i&&rst[i].v-rst[j].v>k) del(rst[j++]); if (add(rst[i]),tot==n) ret=min(ret,n-sum); } return ret; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&f[i]); for (i=1;i<=n;++i) scanf("%d",&d[i]),p[i]=1LL*f[i]*d[i]; for (ans=n,i=1;i<=n;++i) ans=min(ans,solve(p[i])); printf("%d\n",ans); } return 0; }

6|0E. Chain Chips


真是好久没写过DDP(动态DP)的题目了,上次见到可能还是在NOIP2018的D2T3了(乐)

首先吐槽一下做这题的时候由于CF崩了就找了个简述的题面,然后里面说的是n个点n1条边而没说是链,所以刚开始一直在想树上的做法,结果后来没想法再一看题目发现是链

不过其实两者的转化都一样,都是最小权值边覆盖问题,即我们选出若干条边,使得每个点都会被至少一条边覆盖,然后找一种总权值和最小的方案

证明也很简单,如果给出了一种所述的方案,显然很容易构造一组解(通过只交换相邻的元素即可)

而反过来如果存在某个点没有任何一条边覆盖了它,则它一定不可能换成别的芯片,因此原来结论成立

那么我们很容易想到一个naive的DP,设fi.0/1表示选完了前i条边,其中第i条边选/不选的最小代价和,则显然有转移:

fi,0=fi1,1+aifi,1=min(fi1,0,fi1,1)+ai

然后我们把这个转移写成DDP的矩阵形式有:

[fi1,0,fi1,1]×[,ai0,ai]=[fi,0,fi,1]

然后用线段树维护一下即可,总复杂度O(nlogn)

不过要注意一个地方,在维护这种最值的转移矩阵时要写成右乘的形式不然会出问题,调了样例半天才发现

如果一定要写成用列向量左乘转移矩阵的话就要从右边转移到左边,这点一定要注意,在序列上还好,在树剖的时候就很容易搞错顺序

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=200005; const long long INF=5e17; struct Matrix { int n,m; long long mat[2][2]; inline Matrix(CI N=2,CI M=2) { n=N; m=M; for (RI i=0,j;i<n;++i) for (j=0;j<m;++j) mat[i][j]=INF; } inline long long* operator [] (CI x) { return mat[x]; } inline void init(CI mv) { mat[0][0]=INF; mat[1][0]=0; mat[0][1]=mat[1][1]=mv; } friend inline Matrix operator * (Matrix A,Matrix B) { Matrix C(A.n,B.m); for (RI i=0,j,k;i<C.n;++i) for (j=0;j<C.m;++j) for (k=0;k<A.m;++k) C[i][j]=min(C[i][j],A[i][k]+B[k][j]); return C; } }; int n,a[N],q,k,x; class Segment_Tree { private: Matrix t[N<<2]; inline void pushup(CI now) { t[now]=t[now<<1]*t[now<<1|1]; } public: #define TN CI now=1,CI l=2,CI r=n-1 #define LS now<<1,l,mid #define RS now<<1|1,mid+1,r inline void build(TN) { if (l>r) return; if (l==r) return t[now].init(a[l]); int mid=l+r>>1; build(LS); build(RS); pushup(now); } inline void updata(CI pos,TN) { if (l>r) return; if (l==r) return t[now].init(a[l]); int mid=l+r>>1; if (pos<=mid) updata(pos,LS); else updata(pos,RS); pushup(now); } inline Matrix query(void) { if (2>n-1) return Matrix(); return t[1]; } #undef TN #undef LS #undef RS }SEG; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d",&n),i=1;i<n;++i) scanf("%d",&a[i]); for (scanf("%d",&q),SEG.build(),i=1;i<=q;++i) { scanf("%d%d",&k,&x); a[k]=x; if (k>1) SEG.updata(k); Matrix A(1,2); A[0][1]=a[1]; A=A*SEG.query(); printf("%lld\n",2LL*(n!=2?A[0][1]:a[1])); } return 0; }

7|0F. Communication Towers


我已经属于变成那种连线段树分治这种东西的名字都想不起来的老年菜鸡了,只能看看别人的题解了

首先我们发现如果对于接收频率进行线段树分治,那么每条边只会出现在O(nlogn)个线段树节点上

一个naive的想法就是用可撤销并查集维护到1的联通块,但是这样在叶子节点统计答案的时候可能会退化成O(n2)

不过有一种好方法就是先在叶子节点打一个标记,然后在栈中撤销的时候把这个标记传回去即可

但是转念一想又有一个问题,如果我们只是简单地用bool类型的数组来维护标记,那么在涉及到不同的叶子节点带来的影响时可能会搞混掉

而且这个题也没法简单地用时间戳来解决这个问题,因为我们不能进行和赋值有关的操作,不然在可能会用一个叶子的信息覆盖掉另一个叶子的信息

那么改怎么处理呢,有一个很trick性的做法就是打差分标记,具体实现可以看代码,我只能说是惊为天人

那么这题就做完了,用按秩合并维护带撤销并查集,总复杂度O(nlog2n)

#include<cstdio> #include<iostream> #include<vector> #include<utility> #define RI register int #define CI const int& using namespace std; typedef pair <int,int> pi; const int N=200005; struct Data { int x,y,szy; inline Data(CI X=0,CI Y=0,CI SZY=0) { x=X; y=Y; szy=SZY; } }stk[N*20]; int n,m,l[N],r[N],x,y,top,fa[N],sz[N],tag[N]; inline int getfa(CI x) { return fa[x]!=x?getfa(fa[x]):x; } inline void merge(int x,int y) { x=getfa(x); y=getfa(y); if (x==y) return; if (sz[x]>sz[y]) swap(x,y); stk[++top]=Data(x,y,sz[y]); fa[x]=y; sz[y]+=sz[x]==sz[y]; tag[x]-=tag[y]; } inline void revoke(CI tmp) { while (top>tmp) { int x=stk[top].x,y=stk[top].y,szy=stk[top].szy; --top; fa[x]=x; sz[y]=szy; tag[x]+=tag[y]; } } class Segment_Tree { private: vector <pi> v[N<<2]; public: #define TN CI now=1,CI l=1,CI r=200000 #define LS now<<1,l,mid #define RS now<<1|1,mid+1,r inline void insert(CI beg,CI end,const pi& it,TN) { if (beg<=l&&r<=end) return v[now].push_back(it); int mid=l+r>>1; if (beg<=mid) insert(beg,end,it,LS); if (end>mid) insert(beg,end,it,RS); } inline void solve(TN) { int tmp=top; for (auto [x,y]:v[now]) merge(x,y); if (l==r) return ++tag[getfa(1)],revoke(tmp); int mid=l+r>>1; solve(LS); solve(RS); revoke(tmp); } #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),i=1;i<=n;++i) scanf("%d%d",&l[i],&r[i]),fa[i]=i,sz[i]=1; for (i=1;i<=m;++i) { scanf("%d%d",&x,&y); int L=max(l[x],l[y]),R=min(r[x],r[y]); if (L<=R) SEG.insert(L,R,make_pair(x,y)); } for (SEG.solve(),i=1;i<=n;++i) if (tag[i]) printf("%d ",i); return 0; }

8|0Postscript


发现Edu的场题目其实很不错的说,而且总能学到点新trick,下次有机会要积极打一下的说


__EOF__

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