隐藏页面特效

2024“钉耙编程”中国大学生算法设计超级联赛(2)

1|0Preface


最唐氏的一集,前中期被 A 卡得数次破防红温,后期经典不知道在干嘛摆着摆着就结束了

可惜的是徐神最后 1h 写的 B 因为两个数组搞反了一直没过,赛后看了眼就过了,这下狠狠地掉 Rating 了


2|0鸡爪


丁真构造题,但有人连 WA 三发怎么回事呢

首先不难想到最大化和 1 连边的数量,首先可以以 1 为中心搞一个鸡爪,剩下的鸡爪都可以蹭一条和 1 的边

手玩一下会发现此时边数最多为 2+n3+nmod3 条,而剩下的边数总是偶数

此时要在剩下的点间合理地选出若干个鸡爪,可以考虑这样两条两条的加边:

(2,3),(2,4) (2,5).(3,4) (2,6),(3,5)

不难发现这样显然保证了字典序最小

#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #define RI register int #define CI const int& using namespace std; typedef pair <int,int> pi; int t,n; int main() { for (scanf("%d",&t);t;--t) { RI i; scanf("%d",&n); int e1; vector <pi> ans; if (n<=2) e1=n; else e1=2+n/3+n%3; for (i=2;i<=e1+1;++i) ans.push_back(pi(1,i)); int rem=n-e1; if (rem>0) { ans.push_back(pi(2,3)); ans.push_back(pi(2,4)); rem-=2; } for (i=5;i<=e1+1&&rem>0;++i) { ans.push_back(pi(2,i)); ans.push_back(pi(3,i-1)); rem-=2; } sort(ans.begin(),ans.end()); for (auto [x,y]:ans) printf("%d %d\n",x,y); } return 0; }

3|0梦中的地牢战斗


很裸的大力状压题,不难发现用三元组 (x,y,mask) 表示当前角色位于 (x,y),剩下没打死的怪物状压状态为 mask 的最大收益

预处理转移后可以得到一个有向图,但由于可能会成环因此需要用 SPFA 求最长路,细节比较多很容易写挂

#include <bits/stdc++.h> std::tuple<int, int, int> decomp(int status) { return { status & 31, status >> 5 & 31, status >> 10 }; } int comp(int x, int y, int e) { return (x) | (y << 5) | (e << 10); } int dis[1 << 20], vis[1 << 20], dmg[1 << 20]; int ex[10], ey[10], ea[10], eb[10], ec[10]; int U[30][30][10], D[30][30][10], L[30][30][10], R[30][30][10], eas[1 << 10]; int n, m, k, d; inline int L1(int x1, int y1, int x2, int y2) { return std::abs(x1 - x2) + std::abs(y1 - y2); } int work() { std::cin >> n >> m >> k >> d; int emap[30][30]; memset(emap, 0, sizeof(emap)); for(int i = 0; i < k; ++i) std::cin >> ex[i] >> ey[i], emap[--ex[i]][--ey[i]] |= 1 << i, std::cin >> ea[i] >> eb[i] >> ec[i]; for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) for(int k = 0; k <= d; ++k) U[i][j][k] = D[i][j][k] = L[i][j][k] = R[i][j][k] = emap[i][j]; // U[i][j][1] = D[i][j][1] = L[i][j][1] = R[i][j][1] =*/ 0; for(int z = 1; z <= d; ++z) for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) { if(i > 0) U[i][j][z] = U[i - 1][j][z - 1] | emap[i][j]; if(i < n - 1) D[i][j][z] = D[i + 1][j][z - 1] | emap[i][j]; if(j > 0) L[i][j][z] = L[i][j - 1][z - 1] | emap[i][j]; if(j < m - 1) R[i][j][z] = R[i][j + 1][z - 1] | emap[i][j]; } memset(eas, 0, sizeof(eas)); for(int i = 0; i < (1 << k); ++i) { eas[i] = 0; for(int j = 0; j < k; ++j) if(i >> j & 1) eas[i] += ea[j]; } memset(vis, 0, sizeof(vis)); memset(dis, 0x80, sizeof(dis)); for(int x = 0; x < n; ++x) for(int y = 0; y < m; ++y) for(int e = 0; e < (1 << k); ++e) { int c = comp(x, y, e); dmg[c] = 0; for(int z = 0; z < k; ++z) if((e >> z & 1) && L1(x, y, ex[z], ey[z]) <= ec[z]) dmg[c] += eb[z]; // std::cerr << "dmg[" << x << "][" << y << "][" << e << "] = " << dmg[c] << std::endl; } int sx, sy; std::cin >> sx >> sy; sx--, sy--; int ans = 0; int start = comp(sx, sy, (1 << k) - 1); dis[start] = 0; vis[start] = 1; std::queue<int> q; q.push(start); while(!q.empty()) { int id = q.front(); q.pop(); if(dis[id] > ans) ans = dis[id]; vis[id] = false; auto [x, y, e] = decomp(id); assert(!(e & emap[x][y])); // std::cerr << "dis[" << x << "][" << y << "][" << e << "] = " << dis[id] << char(10); auto update = [&](int nid, int r) { if(dis[id] + r > dis[nid]) { dis[nid] = dis[id] + r; if(!vis[nid]) { vis[nid] = true; q.push(nid); } } }; for(int i = 1; i <= d; ++i) { int nx, ny, ne, nid, r; nx = x - i, ny = y, ne = e ^ (e & U[x][y][i]); if(nx >= 0 && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & U[x][y][i]] - dmg[nid], update(nid, r); nx = x + i, ny = y, ne = e ^ (e & D[x][y][i]); if(nx < n && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & D[x][y][i]] - dmg[nid], update(nid, r); ny = y - i, nx = x, ne = e ^ (e & L[x][y][i]); if(ny >= 0 && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & L[x][y][i]] - dmg[nid], update(nid, r); ny = y + i, nx = x, ne = e ^ (e & R[x][y][i]); if(ny < m && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & R[x][y][i]] - dmg[nid], update(nid, r); } } return ans; } int main() { std::ios::sync_with_stdio(false); int T; std::cin >> T; while(T--) { std::cout << work() << std::endl; } return 0; }

4|0绝对不模拟的简单魔方


魔方大师祁神发现这题根本不用 Care 什么旋转,角块的排布就是符合一定拓扑性质的,然后秒了此题

#include<bits/stdc++.h> using namespace std; const int corner[8][3][2] = { {{3, 4}, {4, 3}, {4, 4}}, {{3, 6}, {4, 6}, {4, 7}}, {{1, 6}, {4, 9}, {4, 10}}, {{1, 4}, {4, 12}, {4, 1}}, {{7, 4}, {6, 4}, {6, 3}}, {{7, 6}, {6, 7}, {6, 6}}, {{9, 6}, {6, 10}, {6, 9}}, {{9, 4}, {6, 1}, {6, 12}}, }; int t; string cube[10]; int gp(int c, int p){ auto [x, y] = corner[c][p]; return cube[x][y]-'0'; } int getnum(int c){ return gp(c, 0)*100 + gp(c, 1)*10 + gp(c, 2); } void rorate(int &x){ int tmp = x%10; x /= 10; x += tmp*100; } void solve(){ set<int> st({123, 134, 145, 152, 632, 643, 654, 625}); for (int i=1; i<=9; ++i){ cin >> cube[i]; cube[i] = '$' + cube[i]; } bool ok=true; int ans=0; for (int c=0; c<8; ++c){ int x = getnum(c); while ((x/100 != 1) && (x/100 != 6)) rorate(x); if (!st.count(x)){ok=false; ans=x; break;} } if (ok) cout << "No problem\n"; else{ vector<int> vec({ans/100, (ans/10)%10, ans%10}); sort(vec.begin(), vec.end()); cout << vec[0] << ' ' << vec[1] << ' ' << vec[2] << '\n'; } } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> t; while (t--) solve(); return 0; }

5|0a*b problem


不可做题,弃疗


6|0小塔的养成游戏之梦


赛时没几个队过,弃疗


7|0传奇勇士小凯


根据题意就是选一条从根到叶子的路径,然后简单推一下期望的式子会发现每个点的贡献就是 15pi

手写一个分数类,不难发现只有加法的情况分母不会超过 LCM(1,2,,15),可以直接求解无需高精度

#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=100005; struct frac { int x,y; inline frac(CI X=0,CI Y=1) { int g=__gcd(X,Y); x=X/g; y=Y/g; } friend inline bool operator < (const frac& A,const frac& B) { return A.x*B.y<A.y*B.x; } friend inline frac operator + (const frac& A,const frac& B) { int X=A.x*B.y+A.y*B.x,Y=A.y*B.y,g=__gcd(X,Y); return frac(X/g,Y/g); } }f[N]; int t,n,x,y,a[N]; vector <int> v[N]; inline void DFS(CI now=1,CI fa=0) { f[now]=frac(15,a[now]); frac tmp; for (auto to:v[now]) if (to!=fa) { DFS(to,now); if (tmp<f[to]) tmp=f[to]; } f[now]=f[now]+tmp; } signed main() { for (scanf("%lld",&t);t;--t) { RI i; for (scanf("%lld",&n),i=1;i<=n;++i) v[i].clear(); for (i=1;i<n;++i) scanf("%lld%lld",&x,&y),v[x].push_back(y),v[y].push_back(x); for (i=1;i<=n;++i) scanf("%lld",&a[i]); DFS(); printf("%lld/%lld\n",f[1].x,f[1].y); } return 0; }

8|0URL划分


签到,我题意都不知道

#include <bits/stdc++.h> int main() { int T; std::cin >> T; while(T--) { std::string s; std::cin >> s; int i = 0; while(s[i] != ':') i++; std::cout << s.substr(0, i) << char(10); i += 3; int pre = i; while(s[i] != '/') i++; std::cout << s.substr(pre, i - pre) << char(10); for(;;) { pre = i + 1; if(pre >= s.size()) break; bool flag = false; i = pre; while(s[i] != '/') { if(s[i] == '=') flag = true; i += 1; } if(flag) std::cout << s.substr(pre, i - pre) << char(10); } } return 0; }

9|0成长,生命,幸福


很有趣的一个题

首先要观察到一个重要的性质,即一棵树成长一次后每个点的度数一定是 1/2/3

度数为 1 的一定是叶子不用管,度数为 2 的每次会分裂出两个度数为 2 的,度数为 3 的每次会分裂出一个度数为 3 的和两个度数为 2

考虑用一个二元组 (x,y) 表示一条有 x 个度数为 2 的点,y 个度数为 3 的点的路径,则进行 m1 次成长后其贡献为 2m1×x+(2m1)×y

不难发现在模意义下不能直接比较两个二元组的大小,但我们发现这两个系数在 m 增大时会无限趋近于 12

因此当 m20 时,可以直接用对应的系数带进去比较;否则直接把系数定为 12 算出最大的 (x,y) 再计算答案即可

#include<cstdio> #include<iostream> #include<vector> #define RI register int #define CI const int& using namespace std; typedef long long LL; const int N=100005,mod=1e9+7; int t,n,m,x,y,A,B; vector <int> v[N]; 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 ifo { int x,y; inline ifo(CI X=0,CI Y=0) { x=X; y=Y; } inline LL val(void) { return 1LL*A*x+1LL*B*y; } friend inline ifo operator + (const ifo& A,const ifo& B) { return ifo(A.x+B.x,A.y+B.y); } }ans,f[N],mx[N],smx[N]; inline void DFS(CI now=1,CI fa=0) { if (v[now].size()>=2) f[now]=ifo(2,(int)v[now].size()-2); else f[now]=ifo(); mx[now]=smx[now]=ifo(); for (auto to:v[now]) if (to!=fa) { DFS(to,now); if (f[to].val()>mx[now].val()) smx[now]=mx[now],mx[now]=f[to]; else if (f[to].val()>smx[now].val()) smx[now]=f[to]; } if (f[now].val()+mx[now].val()+smx[now].val()>ans.val()) ans=f[now]+mx[now]+smx[now]; f[now]=f[now]+mx[now]; } int main() { for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++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); if (m<=20) A=1<<m-1,B=(1<<m)-1; else A=1,B=2; ans=ifo(); DFS(); A=quick_pow(2,m-1); B=(quick_pow(2,m)-1+mod)%mod; printf("%d\n",(ans.val()%mod+2)%mod); } return 0; }

10|0强攻计策


不可做题,弃疗


11|0女神的睿智


纯纯的签到

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int t; char s[10]; int main() { for (scanf("%d",&t);t;--t) { scanf("%s",s+1); if (s[1]==s[5]) printf("%c\n",s[1]); else { int c[2]={0,0}; for (RI i=1;i<=8;++i) { if (s[i]==s[1]) ++c[0]; if (s[i]==s[5]) ++c[1]; } if (c[0]==c[1]) puts("N"); else printf("%c\n",c[0]>c[1]?s[1]:s[5]); } } return 0; }

12|0在 A 里面找有 C 的 B


题意都不知道的字符串,被徐神一眼秒了,好像是个 AC 自动机

#include <bits/stdc++.h> struct node_t { int go[26]; int fail, tag; node_t() { memset(this, 0, sizeof(node_t)); } }; auto build_acam( const std::vector<std::string> &s ) -> std::tuple< std::vector<node_t>, std::vector<int>, std::vector<int> > { std::vector<node_t> res = { node_t() }; std::vector<int> pos = {}; for(auto s: s) { int cur = 0; for(auto c: s) { int u = c - 'a'; if(!res[cur].go[u]) { res[cur].go[u] = res.size(); res.push_back(node_t()); } cur = res[cur].go[u]; } pos.push_back(cur); } std::vector<int> q; int ql = 0; for(int i = 0; i < 26; ++i) if(res[0].go[i]) q.push_back(res[0].go[i]); while(ql < q.size()) { int hd = q[ql++]; for(int i = 0; i < 26; ++i) if(res[hd].go[i]) res[res[hd].go[i]].fail = res[res[hd].fail].go[i], q.push_back(res[hd].go[i]); else res[hd].go[i] = res[res[hd].fail].go[i]; } return { res, pos, q }; } std::vector<int> work() { int n; std::cin >> n; std::string a, c; std::vector<std::string> b1(n); std::vector<std::string> b2(n); std::cin >> a >> c; for(int i = 0; i < n; ++i) std::cin >> b1[i] >> b2[i]; std::vector<bool> is_ans(n, true); { auto [acam, pos, q] = build_acam(b1); std::reverse(q.begin(), q.end()); for(int i = 0, cur = 0; i < a.size(); ++i) { int u = a[i] - 'a'; cur = acam[cur].go[u]; acam[cur].tag = 1; } for(auto i: q) acam[acam[i].fail].tag |= acam[i].tag; for(int i = 0; i < n; ++i) is_ans[i] = is_ans[i] && acam[pos[i]].tag; } { auto [acam, pos, q] = build_acam({c}); for(auto pos: pos) acam[pos].tag = 1; for(auto q: q) acam[q].tag |= acam[acam[q].fail].tag; for(int i = 0; i < n; ++i) { int cur = 0; bool flag = false; for(auto c: b2[i]) { cur = acam[cur].go[c - 'a']; flag = flag || acam[cur].tag; } is_ans[i] = is_ans[i] && flag; } } std::vector<int> ans; for(int i = 0; i < n; ++i) if(is_ans[i]) ans.push_back(i); return ans; } int main() { std::ios::sync_with_stdio(false); int T; std::cin >> T; while(T--) { std::vector<int> ans = work(); if(ans.empty()) std::cout << char(10); for(int i = 0; i < ans.size(); ++i) std::cout << ans[i] + 1 << char(i == ans.size() - 1 ? 10 : 32); } return 0; }

13|0图计算


唉经典原题大战,但这次是没做过的原题

考虑如何判断两个点 x,yd+1 张图内都连通,我们可以给每个点求出一个长为 d+1 的向量,其中第 i 个元素代表该点在第 i 个图内的祖先,此时 x,y 连通当且仅当它们对应的向量相同

用 Hash 维护每个点的向量,每次修改时在对应的图中启发式合并一下,将更新父亲节点的点对应的 Hash 值一并修改即可

总复杂度 O(klog2n+m×d)

#include<cstdio> #include<iostream> #include<random> #include<map> #define RI register int #define CI const int& using namespace std; typedef unsigned long long u64; const int N=50005; mt19937 rng; uniform_int_distribution <u64> dist(0,1ull<<63); int t,n,m,d,k,x[N<<1],y[N<<1]; u64 hsh[N]; long long ans; map <u64,int> rst; inline void upt(const u64& x,CI y) { ans-=1LL*rst[x]*(rst[x]-1)/2LL; rst[x]+=y; ans+=1LL*rst[x]*(rst[x]-1)/2LL; } struct DSU { int fa[N]; vector <int> v[N]; u64 seed; inline void init(CI n) { seed=dist(rng); for (RI i=1;i<=n;++i) fa[i]=i,v[i]={i},hsh[i]+=u64(i)*seed; } inline void merge(int x,int y) { x=fa[x]; y=fa[y]; if (x==y) return; if (v[x].size()<v[y].size()) swap(x,y); for (auto u:v[y]) { fa[u]=x; upt(hsh[u],-1); hsh[u]-=u64(y)*seed; hsh[u]+=u64(x)*seed; upt(hsh[u],1); v[x].push_back(u); } v[y].clear(); } }G[105]; int main() { //freopen("1012.in","r",stdin); freopen("my.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j; scanf("%d%d%d%d",&n,&m,&d,&k); for (i=1;i<=n;++i) hsh[i]=0; for (i=1;i<=m;++i) scanf("%d%d",&x[i],&y[i]); for (i=1;i<=d+1;++i) G[i].init(n); for (ans=0,rst.clear(),i=1;i<=n;++i) upt(hsh[i],1); for (i=1;i<=d+1;++i) for (j=1;j<=m;++j) G[i].merge(x[j],y[j]); for (i=1;i<=k;++i) { int u,v,w; scanf("%d%d%d",&u,&v,&w); G[w].merge(u,v); printf("%lld\n",ans); } } return 0; }

14|0Postscript


感觉现在一到后期就开始白兰摸鱼,一点作用都没有,怎么回事呢


__EOF__

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