隐藏页面特效

The 2023 CCPC (Qinhuangdao) Onsite (The 2nd Universal Cup. Stage 9: Qinhuangdao)

1|0Preface


完全披萨,最害怕的一集,2h过了5题后开始大坐牢环节

徐神开D感觉是个巨复杂的字符串讨论题,一不注意就码了200+行

然后我和祁神在下面讨论得出了I的做法,虽然是个DS题但上去写的时候一点自信没有

最后摸了半天到比赛结束后1min才调出样例,赛后又调了半小时左右才过了这题

唉这就是CCPC的强度吗,Au感觉遥不可及,这场还好前面手速够快不然Ag都保不住


2|0A. Make SYSU Great Again I


签到构造题,按照蛇形向右下填入12n,保证每行每列都有两个数并且它们的gcd1即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int n,k; int main() { RI i; for (scanf("%d%d",&n,&k),i=1;i<2*n;++i) printf("%d %d\n",(i+1)/2,(i+1)/2+(i%2?0:1)); printf("%d %d\n",n,1); int left=k-2*n,x=1,y=1; while (left>0) { auto expand=[&](void) { if (y==n) ++x,y=1; else ++y; }; while (x==y||x%n+1==y) expand(); --left; printf("%d %d\n",x,y); expand(); } return 0; }

3|0B. Yet Another Subsequence Problem


啥玩意,看题解好像是个类欧,做不了一点


4|0C. Palindrome


究竟是什么样的字符串题能让string master徐神也折戟沉沙,原来是Hash+分类讨论+DS那没事了


5|0D. Yet Another Coffee


这题我们队VP的时候集体犯病,搞了个相比于正解做法复杂很多的东西,但好在是给他莽过去了

考虑对于一种购买咖啡的方案,能用哪些优惠券其实只和购买的咖啡中最小的下标有关

可以定义一个双变量函数f(x,y),表示钦定构造的编号最小的咖啡为x,一共买了y杯咖啡时的最小花费,其计算方法就是用ax减去所有能用的优惠劵(记为gi),再加上i[x+1,n]中最小的kai之和

显然当x固定时f(x,y)可以用主席树快速求值,现在考虑x变化带来的影响

注意到对于两个方案f(x1,)f(x2,),若x1<x2gx1<gx2,那么显然对于任意的y,均有f(x1,y)<f(x2,y),此时我们可以直接不考虑x2

因此可以从后往前用一个单调栈找出所有可能成为答案的xi,用一个指针一路单调地扫过去即可

总复杂度O(nlogn)

#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #include<cctype> #define int long long #define RI register int #define CI const int& using namespace std; const int N=200005; int t,n,m,a[N],r,w,g[N],stk[N],rt[N],idx; vector <int> rst; #define Tp template <typename T> class FileInputOutput { private: static const int S=1<<21; #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++) #define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch)) char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[30]; public: inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; } Tp inline void read(T& x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); } Tp inline void write(T x,const char ch='\n') { if (x<0) x=-x,pc('-'); RI ptop=0; while (pt[++ptop]=x%10,x/=10); while (ptop) pc(pt[ptop--]+48); pc(ch); } inline void flush(void) { fwrite(Fout,1,Ftop-Fout,stdout); } #undef tc #undef pc }F; class Segment_Tree { private: struct segment { int ch[2],sz,sum; }O[N*20]; public: inline void insert(int& x,CI y,CI pos,CI l=0,CI r=rst.size()-1) { O[x=++idx]=O[y]; ++O[x].sz; O[x].sum+=rst[pos]; if (l==r) return; int mid=l+r>>1; if (pos<=mid) insert(O[x].ch[0],O[y].ch[0],pos,l,mid); else insert(O[x].ch[1],O[y].ch[1],pos,mid+1,r); } inline int query(CI now,CI rk,CI l=0,CI r=rst.size()-1) { if (l==r) return rk*rst[l]; int mid=l+r>>1; if (rk<=O[O[now].ch[0]].sz) return query(O[now].ch[0],rk,l,mid); else return O[O[now].ch[0]].sum+query(O[now].ch[1],rk-O[O[now].ch[0]].sz,mid+1,r); } }SEG; signed main() { for (F.read(t);t;--t) { RI i,k; for (F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]),g[i]=0; for (g[n+1]=0,i=1;i<=m;++i) F.read(r),F.read(w),g[1]+=w,g[r+1]-=w; for (i=1;i<=n;++i) g[i]+=g[i-1]; for (i=1;i<=n;++i) g[i]=a[i]-g[i]; int top=0; for (i=n;i>=1;--i) { while (top&&g[stk[top]]>=g[i]) --top; stk[++top]=i; } for (rst.clear(),i=1;i<=n;++i) rst.push_back(a[i]); sort(rst.begin(),rst.end()); for (i=1;i<=n;++i) a[i]=lower_bound(rst.begin(),rst.end(),a[i])-rst.begin(); for (idx=0,rt[n+1]=0,i=n;i>=1;--i) SEG.insert(rt[i],rt[i+1],a[i]); for (k=i=1;k<=n;++k) { while (n-stk[i]+1<k) ++i; auto calc=[&](CI x,CI y) { if (x==n) return 0LL; return SEG.query(rt[x+1],y); }; while (i+1<=top&&g[stk[i+1]]+calc(stk[i+1],k-1)<g[stk[i]]+calc(stk[i],k-1)) ++i; F.write(g[stk[i]]+calc(stk[i],k-1)," \n"[k==n]); } } return F.flush(),0; }

PS:本题正解极其傻逼,分别考虑每张优惠券,显然将其使用在其可用区间中价格最小的那杯咖啡上一定最优,因此直接排个序求和输出即可


6|0E. Coloring Tape


疑似是个防AK题,弃疗


7|0F. Mystery of Prime


没有证明,全是猜测,这就是ACM的本质!

考虑如果现在有相邻的三个数x,y,z,我们如果要修改y的值使得x+y,y+z都为质数,此时需要满足什么条件

有个naive的想法,因为绝大多数的偶数都不是质数,因此如果x,z的奇偶性不同,则最后无论y如何取值x+y,y+z中必然会有一个为偶数,此时显然不可能都为质数

因此我们猜测,当x,z奇偶性相同时,是否一定存在某个y使得x+y,y+z为质数,直觉告诉我们这个很对

事实上有个什么“波利尼亚克猜想”讲的就是这玩意,或者其实我们大可以写个暴力验证一下这个结论的正确性

在这个猜想成立的前提下,要处理这个题就很简单了,注意我们之前忽略了2这一特殊存在,但好在由于序列中所有数均为正数,因此有且仅有1+1=2这一种情况

因此我们大力DP,设fi,0/1/2/3表示已经操作了前i个数,其中第i个数不变/变成1/变成某个偶数/变成某个不为1的奇数时的最少修改次数,转移的话大力分类讨论即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=100005; int n,a[N],f[N][4]; //0: unchanged; 1: to 1; 2: to even; 3: to odd (exclude 1) int main() { RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) for (scanf("%d",&a[i]),j=0;j<4;++j) f[i][j]=1e9; for (f[1][0]=0,f[1][1]=f[1][2]=f[1][3]=i=1;i<n;++i) { auto is_prime=[&](CI x) { for (RI i=2;i*i<=x;++i) if (x%i==0) return false; return true; }; if (is_prime(a[i]+a[i+1])) f[i+1][0]=min(f[i+1][0],f[i][0]); if (a[i]%2==1) f[i+1][2]=min(f[i+1][2],f[i][0]+1); else f[i+1][3]=min(f[i+1][3],f[i][0]+1); if (is_prime(a[i]+1)) f[i+1][1]=min(f[i+1][1],f[i][0]+1); if (is_prime(1+a[i+1])) f[i+1][0]=min(f[i+1][0],f[i][1]); f[i+1][2]=min(f[i+1][2],f[i][1]+1); f[i+1][1]=min(f[i+1][1],f[i][1]+1); if (a[i+1]%2==1) f[i+1][0]=min(f[i+1][0],f[i][2]); f[i+1][1]=min(f[i+1][1],f[i][2]+1); f[i+1][3]=min(f[i+1][3],f[i][2]+1); if (a[i+1]%2==0) f[i+1][0]=min(f[i+1][0],f[i][3]); f[i+1][2]=min(f[i+1][2],f[i][3]+1); } int ans=f[n][0]; for (i=1;i<=3;++i) ans=min(ans,f[n][i]); return printf("%d",ans),0; }

8|0G. Path


好像是个签,我题目都没看

#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e5+5; int n, m, A[N], B[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n >> m; for (int i=1; i<=n; ++i) cin >> A[i]; for (int i=1; i<=m; ++i) cin >> B[i]; int ans=0; for (int i=1; i<n; ++i) ans += abs(A[i+1]-A[i]); for (int i=1; i<m; ++i) ans += abs(B[i+1]-B[i]); cout << ans << '\n'; return 0; }

9|0H. Quake and Rebuild


看着像个DS题,但我一点做不来的说


10|0I. Phony


唉比赛的时候没想到可以线段树分裂,去写了个沟槽的两边同时二分+诡异无比的线段树上二分,细节有点多直接没写出来

这题的思路可以说是一眼,不难想到把每个数x看成二元组(xk,xmodk),则一次操作等价于找到最大的二元组并将其第一个元素减1

很容易想到拿个什么数据结构把所有xk相同的数存到一起,同时按照第一维从大到小处理

我们可以动态的维护一个指针表示最大的那个数据结构中前多少大的元素已经被操作过一次了(这些数实际的第一维的值应该是存储的减去1),然后若当前的最大的结构的第一维和次大的相等了,那么就合并对于的两个数据结构

本来想着用启发式合并multiset的,但后面询问的时候要查第k大,咱也不会pb_ds,这下直接歇逼了

但好在后面发现可以写线段树合并,而且这玩意复杂度还只有一个log,遂马上滚去写

然后发现查询的时候按我的做法会有点问题,就是当前第一维最大的那个线段树中可能有若干个元素是需要实际减去1的,如果次大的线段树的第一维恰好与其相同就需要将它们合并

当时没想到其实权值线段树也是可以按排名分裂的,因此写了个外面二分答案+里面线段树上限定在某个排名区间内二分的几把玩意,最后还tmd跑过去了

秉着能过就是好代码的原则我也懒得写别的做法了,直接把屎山端上来吧

#include<cstdio> #include<iostream> #include<cctype> #include<vector> #include<algorithm> #define RI register int #define CI const int& #define Tp template <typename T> using namespace std; typedef long long LL; const int N=500005; class FileInputOutput { private: static const int S=1<<21; #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++) #define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch)) char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[30]; public: inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; } Tp inline void read(T& x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); } inline void get_char(char& ch) { while (!isalpha(ch=tc())); } Tp inline void write(T x,const char ch='\n') { if (x<0) x=-x,pc('-'); RI ptop=0; while (pt[++ptop]=x%10,x/=10); while (ptop) pc(pt[ptop--]+48); pc(ch); } inline void flush(void) { fwrite(Fout,1,Ftop-Fout,stdout); } #undef tc #undef pc }F; int n,m,rt[N],sz[N],idx; LL k,a[N],x; vector <LL> rst,module; class Segment_Tree { private: struct segment { int ch[2],sz; }O[N*20]; public: inline void insert(int& x,CI pos,CI l=0,CI r=module.size()-1) { if (!x) x=++idx; ++O[x].sz; if (l==r) return; int mid=l+r>>1; if (pos<=mid) insert(O[x].ch[0],pos,l,mid); else insert(O[x].ch[1],pos,mid+1,r); } inline int merge(int x,int y,CI l=0,CI r=module.size()-1) { if (!x||!y) return x|y; if (l==r) return O[x].sz+=O[y].sz,x; int mid=l+r>>1; O[x].ch[0]=merge(O[x].ch[0],O[y].ch[0],l,mid); O[x].ch[1]=merge(O[x].ch[1],O[y].ch[1],mid+1,r); O[x].sz=O[O[x].ch[0]].sz+O[O[x].ch[1]].sz; return x; } inline LL find_kth(CI x,CI rk,CI l=0,CI r=module.size()-1) { if (l==r) return module[l]; int mid=l+r>>1; if (rk<=O[O[x].ch[1]].sz) return find_kth(O[x].ch[1],rk,mid+1,r); return find_kth(O[x].ch[0],rk-O[O[x].ch[1]].sz,l,mid); } inline int query(CI x,CI lim,const LL& grt,CI l=0,CI r=module.size()-1) { if (!x||!lim||module[r]<=grt) return 0; if (grt<module[l]) return min(lim,O[x].sz); int mid=l+r>>1; if (lim<=O[O[x].ch[1]].sz) return query(O[x].ch[1],lim,grt,mid+1,r); return query(O[x].ch[1],O[O[x].ch[1]].sz,grt,mid+1,r)+query(O[x].ch[0],lim-O[O[x].ch[1]].sz,grt,l,mid); } }SEG; signed main() { //freopen("I.in","r",stdin); freopen("I.out","w",stdout); RI i; for (F.read(n),F.read(m),F.read(k),i=1;i<=n;++i) F.read(a[i]),rst.push_back(a[i]/k),module.push_back(a[i]%k); sort(rst.begin(),rst.end()); rst.erase(unique(rst.begin(),rst.end()),rst.end()); sort(module.begin(),module.end()); module.erase(unique(module.begin(),module.end()),module.end()); for (i=1;i<=n;++i) { int id=lower_bound(rst.begin(),rst.end(),a[i]/k)-rst.begin(); ++sz[id]; SEG.insert(rt[id],lower_bound(module.begin(),module.end(),a[i]%k)-module.begin()); } vector <LL> pfx(rst.size()); pfx[0]=sz[0]; for (i=1;i<rst.size();++i) pfx[i]=pfx[i-1]+sz[i]; int pos=0,id=rst.size()-1; for (i=1;i<=m;++i) { char opt; F.get_char(opt); F.read(x); if (opt=='C') { while (x>0) { if (pos+x<sz[id]) { pos+=x; x=0; break; } x-=sz[id]-pos; --rst[id]; pos=0; if (id>0) { LL steps=(rst[id]-rst[id-1])*sz[id]; if (steps<=x) { x-=steps; pfx[id-1]+=sz[id]; sz[id-1]+=sz[id]; rt[id-1]=SEG.merge(rt[id-1],rt[id]); --id; } else { rst[id]-=x/sz[id]; pos=x%sz[id]; x=0; } } else { rst[0]-=x/n; pos=x%n; x=0; } } } else { if (x<=sz[id]-pos) { F.write(rst[id]*k+SEG.find_kth(rt[id],pos+x)); continue; } else x-=(sz[id]-pos); if (id>0&&rst[id]-1==rst[id-1]&&x<=pos+sz[id-1]) { LL l=0,r=k-1,mid,ret; while (l<=r) { mid=l+r>>1; if (SEG.query(rt[id],pos,mid)+SEG.query(rt[id-1],sz[id-1],mid)<x) ret=mid,r=mid-1; else l=mid+1; } F.write(rst[id-1]*k+ret); continue; } if (x<=pos) { F.write((rst[id]-1)*k+SEG.find_kth(rt[id],x)); continue; } else x-=pos; int l=0,r=id-1,mid,ret; while (l<=r) { mid=l+r>>1; if (pfx[id-1]-pfx[mid]<x) ret=mid,r=mid-1; else l=mid+1; } F.write(rst[ret]*k+SEG.find_kth(rt[ret],x-(pfx[id-1]-pfx[ret]))); } } return F.flush(),0; }

11|0J. Keyi LIkes Reading


这场题的问题就是Easy太简单,Medium太难,导致前面的题极其傻逼而中期题都开不动

这题的话由于数据范围极小,我们直接大力状压DP,fmask表示把长度状态为mask的单词都学了的最少天数

转移的话可以枚举子集,并预处理每个状态是否可以在一天内看完,总复杂度O(3|ai|)

#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=(1<<13)+5; int n,W,g[N],f[N],x,c[N]; int main() { RI i,j; for (scanf("%d%d",&n,&W),i=1;i<=n;++i) scanf("%d",&x),++c[x-1]; int m=0; for (i=0;i<13;++i) if (c[i]>0) m|=(1<<i); vector <int> states; for (i=m;i;i=(i-1)&m) states.push_back(i); sort(states.begin(),states.end()); for (auto x:states) { int sum=0; for (i=0;i<13;++i) if ((x>>i)&1) sum+=c[i]; if (sum<=W) g[x]=1; } for (auto x:states) { f[x]=1e9; for (i=x;i;i=(i-1)&x) if (g[i]) f[x]=min(f[x],f[x^i]+1); } return printf("%d",f[m]),0; }

12|0K. Make SYSU Great Again II


题解中认为这是本场最难的一题,但实际看来感觉还行,学校的学长队在VP的时候也是直接赛时过了这题

之后有时间遥控祁神去把这题补了(乐


13|0L. Yet Another Maximize Permutation Subarrays


题目都没看,一眼寄


14|0M. Inverted


没啥好说的,智力不足导致和祁神两个人画了半天也找不到关键的转化

后面补题的时候对着官方题解看了半天才看懂,由于里面有很多例图讲的也很详细所以就不细写了

总结就是Counting是一生之敌

#include<cstdio> #include<iostream> #include<vector> #include<cstring> #define RI register int #define CI const int& using namespace std; const int N=5005,mod=998244353; int n,x,y,vis[N],f[N],g[N]; bool act[N]; vector <int> v[N]; inline void DP(CI now,CI fa=0) { if (!act[now]) return (void)(f[now]=1,g[now]=0); f[now]=0; g[now]=1; vis[now]=1; for (auto to:v[now]) if (to!=fa) { DP(to,now); f[now]=(1LL*f[now]*g[to]+1LL*g[now]*f[to]+2LL*f[now]*f[to])%mod; g[now]=(1LL*g[now]*g[to]+2LL*g[now]*f[to])%mod; } } int main() { RI i,j; for (scanf("%d",&n),i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x); for (i=1;i<n;++i) { scanf("%d",&x); act[x]=1; memset(vis,0,sizeof(vis)); int ans=1; for (j=1;j<=n;++j) if (act[j]&&!vis[j]) DP(j),ans=1LL*ans*f[j]%mod; printf("%d\n",ans); } return 0; }

15|0Postscript


唉感觉从去年区域赛的时候到现在一个季度过去了水平也没啥变化的说,好多题该做不来还是做不来,怎么回事呢


__EOF__

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