隐藏页面特效

The 2024 CCPC National Invitational Contest (Changchun) , The 17th Jilin Provincial Collegiate Programming Contest

1|0Preface


又是经典省赛找信心,这场虽然中间经典三开三卡,但最后都调出来了 4h 10 题下班

剩下的 A 对着题解推了下式子感觉好麻烦就白兰了,J 更是经典动态维护凸壳写不了一点


2|0A. Eminor Array


神秘 DP 题,比赛的时候推了几个性质感觉没啥思路就白兰了,后面看了下题解就是耐心地不停化式子,鉴定为寄


3|0B. Dfs Order 0.5


小清新 DP + 贪心,由于 DFS 序的性质很容易想到按照子树划分状态

fx,0/1 表示处理了以 x 为根的子树,其中偶数/奇数位置上的数和的最大值

对于 x 的转移考虑其所有子节点 y,如果每个子树大小都是偶数那么其实贡献是固定的,不随顺序变化

否则假设存在 k 个奇数大小的子树,此时显然所有偶数子树都可以取到 max(fy,0,fy,1)

而奇数子树总是一半取 0,一半取 1,当 k 为奇数时其中一边会多出一个

这是个很经典的贪心问题,我们先强制所有的都选 0,然后按照 fy,1fy,0 的值从大到小排序后选最大的对应个数即可

#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #define int long long #define RI register int #define CI const int& using namespace std; const int N=200005; int t,n,a[N],x,y,sz[N],f[N][2]; vector <int> v[N]; inline void DFS(CI now=1,CI fa=0) { f[now][0]=0; f[now][1]=a[now]; sz[now]=1; int odd_cnt=0; for (auto to:v[now]) if (to!=fa) { DFS(to,now); sz[now]+=sz[to]; odd_cnt+=(sz[to]%2==1); } if (odd_cnt) { int sum_even=0,sum_odd=0; vector <int> cg; for (auto to:v[now]) if (to!=fa) if (sz[to]%2==1) sum_odd+=f[to][0],cg.push_back(f[to][1]-f[to][0]); else sum_even+=max(f[to][0],f[to][1]); sort(cg.begin(),cg.end(),greater <int>()); int token=odd_cnt/2+odd_cnt%2,dlt=0; for (RI i=0;i<token;++i) dlt+=cg[i]; f[now][0]+=sum_even+sum_odd+dlt; token=odd_cnt/2; dlt=0; for (RI i=0;i<token;++i) dlt+=cg[i]; f[now][1]+=sum_even+sum_odd+dlt; } else { for (auto to:v[now]) if (to!=fa) f[now][0]+=f[to][1],f[now][1]+=f[to][0]; } } signed main() { for (scanf("%lld",&t);t;--t) { scanf("%lld",&n); for (RI i=1;i<=n;++i) scanf("%lld",&a[i]),v[i].clear(); for (RI i=1;i<n;++i) scanf("%lld%lld",&x,&y),v[x].push_back(y),v[y].push_back(x); DFS(); printf("%lld\n",f[1][0]); } return 0; }

4|0C. Fibonacci Sum


徐神一波大力推式子直接秒了此题,ORZ

#include <bits/stdc++.h> using llsi = long long signed int; constexpr int $n = 10'000'007; constexpr llsi mod = 1'000'000'007; int fac[$n], facinv[$n]; llsi ksm(llsi a, llsi b) { llsi res = 1; while(b) { if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } void init_fac(int n = 10'000'000) { fac[0] = 1; for(llsi i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % mod; facinv[n] = ksm(fac[n], mod - 2); for(llsi i = n; i >= 0; --i) facinv[i - 1] = facinv[i] * i % mod; return ; } inline llsi C(int a, int b) { return llsi(fac[a]) * facinv[b] % mod * facinv[a - b] % mod; } int f[$n], g[$n], h[$n]; void init_f(int n = 10'000'000) { f[1] = f[2] = 1; for(int i = 3; i <= n; ++i) f[i] = (f[i - 1] + f[i - 2]) % mod; g[1] = 1, g[2] = 3; h[0] = 1, h[1] = 1; h[2] = 2; for(int i = 3; i <= n; ++i) g[i] = (llsi(g[i - 1]) * 3 + mod - g[i - 2]) % mod; for(int i = 3; i <= n; ++i) h[i] = (llsi(h[i - 1]) * 3 + mod - h[i - 2]) % mod; } int n; char s[$n]; int main() { init_fac(); init_f(); scanf("%s", s + 1); n = strlen(s + 1); // for(int i = 1; i <= 10; ++i) printf("%d %d %d\n", f[i], g[i], h[i]); int b = 0; llsi ans = 0; for(int i = 1; i <= n; ++i) { if(s[i] == '1') { ans += (llsi(f[b]) * h[n - i] + llsi(f[b + 1]) * g[n - i]) % mod; b += 1; } else { // Nothing happens } } ans += f[b]; printf("%lld\n", ans % mod); return 0; }

5|0D. Parallel Lines


神秘题,注意到题目保证有解,且每条直线上至少有 2 个点,因此最后答案直线的斜率一定可以由一个点对得到

考虑随机搞一个点对,它是答案的概率显然不会低于 1k,而检验一个答案是否合法可以用扫描线 O(nlogn) 解决

最后大致复杂度为 O(nklogn)

#include<bits/stdc++.h> using namespace std; #define int long long struct Pt{ int x, y; Pt operator-(const Pt &b)const{return Pt{x-b.x, y-b.y};} int dot(const Pt &b)const{return x*b.x+y*b.y;} bool operator<(const Pt &b)const{return x!=b.x ? x<b.x : y<b.y;} Pt rot90()const{return Pt{-y, x};} }; mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); const int N = 1e4+5; int n, k; Pt pt[N]; int randint(int l, int r) { return uniform_int_distribution{l, r}(rnd); } pair<int, int> rpair() { int l = randint(0, n - 1), r; do r = randint(0, n - 1); while(l == r); return {l, r}; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n >> k; for (int i=0; i<n; ++i){ cin >> pt[i].x >> pt[i].y; } while (1){ auto [o, x] = rpair(); Pt ox = pt[x] - pt[o]; Pt v = ox.rot90(); map<int, vector<int>> mp; for (int i=0; i<n; ++i){ int res = v.dot(pt[i]-pt[o]); mp[res].push_back(i); if (mp.size() > k) break; } bool ok=true; if (mp.size()!=k) ok=false; if (ok){ for (auto [_, vec] : mp){ if (vec.size()<2){ok=false; break;} } } if (ok){ for (auto [_, vec] : mp){ cout << vec.size(); for (int x : vec) cout << ' ' << x+1; cout << '\n'; } break; } } return 0; }

6|0E. Connected Components


首先转化题意,令 xi=aii,yi=ibi,则两个点之间有边等价于它们间满足二维偏序关系

考虑按照 xi 从小到大排序,用增量法一个一个地将点加入

可以用一个单调栈来维护之前每个连通块的 yi 最小的元素,对于新加入的点我们钦定栈顶不变然后弹出被合并掉的点即可

实现的时候对于 xi 相同的点之间的处理顺序需要注意,复杂度 O(nlogn)

#include<bits/stdc++.h> using namespace std; const int N = 1e6+5; int n, A[N], B[N], X[N], Y[N], id[N]; int stk[N], top=0; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n; for (int i=1; i<=n; ++i){ cin >> A[i] >> B[i]; X[i] = A[i]-i; Y[i] = i-B[i]; id[i] = i; } sort(id+1, id+n+1, [&](int a, int b){return X[a]!=X[b] ? X[a]<X[b] : Y[a]<Y[b];}); int cur=id[1]; for (int i=2; i<=n; ++i){ if (Y[id[i]] >= Y[cur]){ while (top>0 && Y[id[i]] >= Y[stk[top]]) --top; }else{ if (X[cur]!=X[id[i]]) stk[++top] = cur; cur = id[i]; } } stk[++top] = cur; cout << top << '\n'; return 0; }

7|0F. Best Player


Light DS 题

首先有一个很直观的 O(nm) 做法,枚举最后获胜的人 p,然后枚举每场比赛

如果这场比赛包括 p,则让它拿走所有的分即可;否则每场比赛我们把 zi 尽量平均分配一定最优

有了这个做法后我们发现可以先把所有的比赛都平均分配,然后枚举到人 p 的时候直接把与它有关的比赛修改即可,用 multiset 可以轻松维护

总复杂度 O(nlogn)

#include<cstdio> #include<iostream> #include<set> #include<vector> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=200005; int t,n,m,a[N],b[N],x[N],y[N],z[N],tx[N],ty[N],tz[N]; vector <int> vec[N]; multiset <int> s[N]; int main() { ios::sync_with_stdio(0); cin.tie(0); for (cin>>t;t;--t) { cin>>n>>m; for (RI i=1;i<=n;++i) s[i].clear(),vec[i].clear(); for (RI i=1;i<=m;++i) { cin>>a[i]>>b[i]>>x[i]>>y[i]>>z[i]; tx[i]=x[i]; ty[i]=y[i]; tz[i]=z[i]; vec[a[i]].push_back(i); vec[b[i]].push_back(i); if (x[i]<=y[i]) { int dlt=min(y[i]-x[i],z[i]); x[i]+=dlt; z[i]-=dlt; if (z[i]>0) x[i]+=z[i]/2+(z[i]%2),y[i]+=z[i]/2; s[a[i]].insert(x[i]); s[b[i]].insert(y[i]); } else { int dlt=min(x[i]-y[i],z[i]); y[i]+=dlt; z[i]-=dlt; if (z[i]>0) x[i]+=z[i]/2+(z[i]%2),y[i]+=z[i]/2; s[a[i]].insert(x[i]); s[b[i]].insert(y[i]); } } multiset <int> rst; for (RI i=1;i<=n;++i) rst.insert(*s[i].rbegin()); vector <int> ans; for (RI i=1;i<=n;++i) { vector <int> tmp; for (auto id:vec[i]) tmp.push_back(a[id]),tmp.push_back(b[id]); sort(tmp.begin(),tmp.end()); tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end()); for (auto p:tmp) rst.erase(rst.find(*s[p].rbegin())); for (auto id:vec[i]) { s[a[id]].erase(s[a[id]].find(x[id])); s[b[id]].erase(s[b[id]].find(y[id])); } for (auto id:vec[i]) { if (a[id]==i) { int nx=tx[id]+tz[id],ny=ty[id]; s[a[id]].insert(nx); s[b[id]].insert(ny); } else { int nx=tx[id],ny=ty[id]+tz[id]; s[a[id]].insert(nx); s[b[id]].insert(ny); } } // printf("i = %d\n",i); // for (RI j=1;j<=n;++j) printf("%d ",*s[j].rbegin()); putchar('\n'); for (auto p:tmp) rst.insert(*s[p].rbegin()); auto mx=rst.end(); --mx; auto smx=mx; --smx; if (*mx==*s[i].rbegin()&&*mx!=*smx) ans.push_back(i); for (auto p:tmp) rst.erase(rst.find(*s[p].rbegin())); for (auto id:vec[i]) { if (a[id]==i) { int nx=tx[id]+tz[id],ny=ty[id]; s[a[id]].erase(s[a[id]].find(nx)); s[b[id]].erase(s[b[id]].find(ny)); } else { int nx=tx[id],ny=ty[id]+tz[id]; s[a[id]].erase(s[a[id]].find(nx)); s[b[id]].erase(s[b[id]].find(ny)); } } for (auto id:vec[i]) { s[a[id]].insert(x[id]); s[b[id]].insert(y[id]); } for (auto p:tmp) rst.insert(*s[p].rbegin()); } printf("%d\n",(int)ans.size()); for (auto x:ans) printf("%d ",x); putchar('\n'); } return 0; }

8|0G. Platform Game


签到题,祁神开场写的我题目都没看

#include<bits/stdc++.h> using namespace std; struct Seg{ int l, r, y; bool operator<(const Seg &b)const{return y>b.y;} }; void solve(){ int n; cin >> n; vector<Seg> segs(n); for (int i=0; i<n; ++i){ int l, r, y; cin >> l >> r >> y; segs[i] = Seg{l, r, y}; } sort(segs.begin(), segs.end()); int sx, sy; cin >> sx >> sy; for (auto [l, r, y] : segs){ if (y>sy) continue; if (sx>l && sx<r){ sy = y; sx = r; } } cout << sx << '\n'; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }

9|0H. Games on the Ads 2: Painting


有铸币写拓扑排序写假了爆 TLE 了五发,后面徐神上去给我重新实现了下跑得飞快

首先不难想到根据 ci,jpi,qj 的关系进行分讨:

  • ci,jpici,jqj,则这种情况一定无解;
  • ci,jpici,j=qj,则说明列 j 的染色时间一定晚于行 i
  • ci,j=pici,jqj,则说明行 i 的染色时间一定晚于列 j​;
  • ci,j=pici,j=qj,则行 i 和列 j 的染色时间不定

由于 {p},{q} 都是排列,因此满足第四个条件的点有且仅有 n 个,我们考虑 2n 枚举它们对应的行列的先后信息

这类先后选的问题一眼转为拓扑序计数,即我们用边 xy 表示点 x 要在 y 之前选

朴素的拓扑序计数是 NPC 的,但这题的图是个二分竞赛图,手玩下会发现若当前存在 k 个度数为 0 的点,则不把这些点取完是不会得到新的度数为 0 的点的

因此每次找到度数为 0 的点数量 k,将 k! 计入贡献,然后删去这些点继续处理即可;注意当出现环时强制贡献为 0

总复杂度 O(2n×n2),需要较好的实现

#include<cstdio> #include<iostream> #include<vector> #include<cassert> #define RI register int #define CI const int& using namespace std; const int N=45,mod=998244353; int n,m,ans,p[N],q[N],c[N][N],deg[N],tdeg[N],fact[N],x[N],y[N],num[N][N]; vector <int> v[N]; inline void init(CI n) { fact[0]=1; for (RI i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod; } int main() { //freopen("H.in","r",stdin); init(40); scanf("%d",&n); for (RI i=1;i<=n;++i) scanf("%d",&p[i]); for (RI i=1;i<=n;++i) scanf("%d",&q[i]); for (RI i=1;i<=n;++i) for (RI j=1;j<=n;++j) scanf("%d",&c[i][j]); for (RI i=1;i<=n;++i) for (RI j=1;j<=n;++j) { if (c[i][j]!=p[i]&&c[i][j]!=q[j]) return puts("0"),0; if (c[i][j]==p[i]&&c[i][j]==q[j]) { num[i][j]=m; x[m]=i; y[m]=j; ++m; } else { if (c[i][j]==p[i]) v[n+j].push_back(i),++tdeg[i]; else v[i].push_back(n+j),++tdeg[n+j]; } } assert(m==n); for(int i = 1; i <= 2 * n; ++i) v[i].reserve(n); for (RI mask=0;mask<(1<<m);++mask) { for (RI i=1;i<=2*n;++i) deg[i]=tdeg[i]; for (RI i=0;i<m;++i) if ((mask>>i)&1) v[x[i]].push_back(n+y[i]),++deg[n+y[i]]; else v[n+y[i]].push_back(x[i]),++deg[x[i]]; int ret; // vector <int> pnt; /*for (RI i=1;i<=2*n;++i) pnt.push_back(i); while (!pnt.empty()) { vector <int> vec,rmv; for (auto x:pnt) if (!deg[x]) rmv.push_back(x); else vec.push_back(x); if (rmv.empty()) { ret=0; break; } else ret=1LL*ret*fact[(int)(rmv.size())]%mod; for (auto x:rmv) for (auto y:v[x]) --deg[y]; pnt=vec; }*/ { static int base[2][45]; auto f1 = base[0], f2 = base[1]; int count = 0, cc; for(RI i = 1; i <= 2 * n; ++i) if(deg[i] == 0) f1[count++] = i; ret = fact[count]; cc = count; while(count) { int ncount = 0; for(RI i = 0; i < count; ++i) for(auto v: v[f1[i]]) if(--deg[v] == 0) f2[ncount++] = v; ret = 1LL * ret * fact[ncount] % mod; std::swap(f1, f2); cc += count = ncount; } if(cc < 2 * n) ret = 0; } (ans+=ret)%=mod; for (RI i=m-1;i>=0;--i) if ((mask>>i)&1) v[x[i]].pop_back(); else v[n+y[i]].pop_back(); } return printf("%d",ans),0; }

10|0I. The Easiest Problem


还真是最简单的题,直接 puts("21") 即可

#include<cstdio> using namespace std; int main() { puts("21"); return 0; }

11|0J. Lone Trail


沟槽的动态凸包,直接白兰弃疗


12|0K. String Divide II


string master 徐神直接高阶科技 runs 秒了,30s 时限的题跑 300ms 可海星

#include <bits/stdc++.h> int n, k; char s[1000006]; namespace has { using namespace std; const int mod = 998'244'853, b = 131; int a[1000006]; int pw[1000006], s[1000006]; inline void build(int n) { pw[0] = 1; for (int i = 1; i <= n; i++) { pw[i] = 1ll * pw[i - 1] * b % mod; s[i] = (s[i - 1] + 1ll * pw[i - 1] * a[i]) % mod; } } inline int query(int l, int r) { return s[r] < s[l - 1] ? s[r] + mod - s[l - 1] : s[r] - s[l - 1]; } inline int lcs(int i, int j, int up = 1 << 30) { if (a[i] != a[j]) return 0; if (i > j) swap(i, j); if (!i) return 0; if (1ll * query(1, i) * pw[j - i] % mod == query(j - i + 1, j)) return i; int l = 2, r = min(i, up), ans = 1; if (1ll * query(i - r + 1, i) * pw[j - i] % mod == query(j - r + 1, j)) return r; while (l <= r) { int mid = (l + r) >> 1; if (1ll * pw[j - i] * query(i - mid + 1, i) % mod == 1ll * query(j - mid + 1, j)) ans = mid, l = mid + 1; else r = mid - 1; } return ans; } inline int lcp(int i, int j, int up = 1 << 30) { if (a[i] != a[j]) return 0; if (i > j) swap(i, j); if (j > n) return 0; int l = 2, r = min(up, n - j + 1), ans = 1; if (1ll * query(i, i + r - 1) * pw[j - i] % mod == query(j, j + r - 1)) return r; while (l <= r) { int mid = (l + r) >> 1; if (1ll * pw[j - i] * query(i, i + mid - 1) % mod == query(j, j + mid - 1)) ans = mid, l = mid + 1; else r = mid - 1; } return ans; } } // namespace has using has::lcp; using has::lcs; struct runs { int l, r, p; bool operator < (const runs &j) const { return l != j.l ? l < j.l : r < j.r; } bool operator ==(const runs &j) const { return l == j.l && r == j.r && p == j.p; } }; std::vector<runs> q; inline void get_runs() { static int st[1000001]; st[0] = n + 1; using has::a; for (int i = n, top = 0, lt = 0; i; i--) { while (top) { int x = std::min(st[top] - i, st[top - 1] - st[top]); lt = lcp(i, st[top], x); if ((lt == x && st[top] - i < st[top - 1] - st[top]) || (lt < x && a[i + lt] < a[st[top] + lt])) top--, lt = 0; else break; } int j = st[top]; st[++top] = i; int x = lcs(i - 1, j - 1, n), y; if (x < j - i) { y = lcp(i, j, n); if (x + y >= j - i) { q.push_back(runs{i - x, j + y - 1, j - i}); } } } } int main() { scanf("%d%d", &n, &k); scanf("%s", s + 1); for (int i = 1; i <= n; i++) has::a[i] = 25 - (s[i] - 'a'); has::build(n), get_runs(); for (int i = 1; i <= n; i++) has::a[i] = 25 - has::a[i]; has::build(n), get_runs(); int ans = 0; for(auto [l, r, p]: q) { // printf("[%d, %d, %d]\n", l, r, p); int len = r - l + 1, part = len / p; ans = std::max(ans, part / k * k * p); } printf("%d\n", ans); return 0; }

13|0L. Recharge


简单贪心分讨题,优先用 2 然后如果 k 是奇数就补上一个 1,直到把某一种数用完

#include<cstdio> #include<iostream> #define int long long #define RI register int #define CI const int& using namespace std; const int INF=1e18; int t,k,x,y; signed main() { for (scanf("%lld",&t);t;--t) { scanf("%lld%lld%lld",&k,&x,&y); int c2=k/2,c1=k-c2*2,c=min(c1?x/c1:INF,c2?y/c2:INF); int ans=c; x-=c1*c; y-=c2*c; while (x>0&&y>0&&x+2*y>=k) { int c2=min(k/2,y),tmp=c2*2; y-=c2; if (k-tmp<=x) x-=k-tmp,++ans; else if ((k-tmp+1)/2<=y) y-=(k-tmp+1)/2,++ans; } if (x) ans+=x/k; else ans+=y/((k+1)/2); printf("%lld\n",ans); } return 0; }

14|0Postscript


多校我唯唯诺诺,省赛我重拳出击,这就是虐菜大师啊


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18352681.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(593)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2023-08-10 UESTC 2023 Summer Training #25 div2/2021 HDU Multi-University Training Contest 7
2018-08-10 NOI Day1线上同步赛梦游记
点击右上角即可分享
微信分享提示