隐藏页面特效

2020-2021 ICPC NERC (NEERC), North-Western Russia Regional Contest (Northern Subregionals)

1|0Preface


最近事情挺多积压了三天没训练,本来计划着去补CF的题,但后面还是溜去写WA2杂谈了

今天重归训练结果碰上超级毒瘤场,2h30min9题后剩下题目都难得飞起,最后还是靠徐神大力切了L题(我早就提前下班了)

评价是这就是Russia场的威力吗,后面的Hard题是一点不可做啊


2|0A. Archivist


纯签到

#include<cstdio> #include<iostream> using namespace std; int main() { int y; scanf("%d",&y); switch (y) { case 1995: puts("ITMO"); break; case 1996: puts("SPbSU"); break; case 1997: puts("SPbSU"); break; case 1998: puts("ITMO"); break; case 1999: puts("ITMO"); break; case 2000: puts("SPbSU"); break; case 2001: puts("ITMO"); break; case 2002: puts("ITMO"); break; case 2003: puts("ITMO"); break; case 2004: puts("ITMO"); break; case 2005: puts("ITMO"); break; case 2006: puts("PetrSU, ITMO"); break; case 2007: puts("SPbSU"); break; case 2008: puts("SPbSU"); break; case 2009: puts("ITMO"); break; case 2010: puts("ITMO"); break; case 2011: puts("ITMO"); break; case 2012: puts("ITMO"); break; case 2013: puts("SPbSU"); break; case 2014: puts("ITMO"); break; case 2015: puts("ITMO"); break; case 2016: puts("ITMO"); break; case 2017: puts("ITMO"); break; case 2018: puts("SPbSU"); break; case 2019: puts("ITMO"); break; } return 0; }

3|0B. Bicycle


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

a = int(input()) x = int(input()) b = int(input()) y = int(input()) a = [a, b] x = [x, y] b = [30, 45] T = int(input()) ans = [] for i in [0, 1]: upd = a[i] if T >= b[i]: upd += (T - b[i]) * x[i] * 21 ans += [upd] print("%d %d" % (ans[0], ans[1]))

4|0C. Corrupted Sort


ORZ徐神,鸡尾酒排序秒了

考虑如果我们已经得到了一个有序的数组,对其随机交换两项后,怎样在2n步之内还原数组

做法是先从前往后冒泡一遍(把较大的数归位),再从后往前冒泡一遍(把较小的数归位),此时操作数量2(n1)恰好不会引发交换

实现的时候由于上限比较宽松可以直接重复上述操作

#include <bits/stdc++.h> int main() { std::ios::sync_with_stdio(false); int n; std::cin >> n; std::string s(""); #define IF_WIN std::cin >> s; if(s == "WIN") exit(0) for(;;) { for(int i = 1; i < n; ++i) { std::cout << i << ' ' << i + 1 << std::endl; IF_WIN; } for(int i = n; i > 1; --i) { std::cout << i - 1 << ' ' << i << std::endl; IF_WIN; } } return 0; }

5|0D. Display


题目转化一下就是给出若干个h维向量,求个数最少的向量使得它们的和中的某一维超过s

枚举最后超出的是哪一维,很显然只选择这一维最大的那个向量是最优的

#include<cstdio> #include<iostream> #include<string> #define RI register int #define CI const int& using namespace std; const int N=105; int n,w,h,s,mx[N],ans_len=1e9; char ch[N],ans_ch; int main() { ios::sync_with_stdio(false); cin.tie(0); RI i,j,k; for (cin>>n>>w>>h>>s,i=1;i<=n;++i) { string cur; cin>>cur; for (j=1;j<=h;++j) { string row; cin>>row; row="."+row+"."; int cnt=0; for (k=1;k<row.size();++k) if (row[k]!=row[k-1]) ++cnt; if (cnt>mx[j]) mx[j]=cnt,ch[j]=cur[0]; } } for (i=1;i<=h;++i) { if (mx[i]==0) continue; int tmp=(s+mx[i]-1)/mx[i]; if (tmp<ans_len) ans_len=tmp,ans_ch=ch[i]; } for (i=1;i<=ans_len;++i) cout<<ans_ch; return 0; }

6|0E. Easy Compare-and-Set


首先发现可以先处理wi=1的操作,wi=0的操作最后讨论一下即可

离散化后把数值看作点,一个wi=1操作看作一条有向边,则题目等价于找一个从c出发的路径经过所有边恰好一次

注意到这等价于图存在欧拉回路/存在以c为起点的欧拉通路

有向图存在欧拉回路的条件:所有点出度入度相同;有向图存在欧拉通路的条件:有且仅有一个点出度比入度大1(起点),有且仅有一个点入度比出度大1(终点)

另外还需要判断图是否连通,具体实现看代码

#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #define RI register int #define CI const int& #define fi first #define se second using namespace std; typedef pair <int,int> pi; const int N=200005; int n,c,a[N],b[N],w[N],in[N],out[N],fa[N],vis[N]; vector <pi> v[N]; vector <int> path; inline int getfa(CI x) { return fa[x]!=x?fa[x]=getfa(fa[x]):x; } inline void addedge(CI x,CI y,CI z) { v[x].push_back(pi(y,z)); ++out[x]; ++in[y]; } inline void DFS(CI now) { while (!v[now].empty()) { auto [to,id]=v[now].back(); v[now].pop_back(); if (vis[id]) continue; DFS(to); vis[id]=1; path.push_back(id); } } int main() { RI i; scanf("%d%d",&n,&c); vector <int> rst; for (rst.push_back(c),i=1;i<=n;++i) scanf("%d%d%d",&a[i],&b[i],&w[i]),rst.push_back(a[i]),rst.push_back(b[i]); sort(rst.begin(),rst.end()); rst.erase(unique(rst.begin(),rst.end()),rst.end()); auto find=[&](CI x) { return lower_bound(rst.begin(),rst.end(),x)-rst.begin()+1; }; for (c=find(c),i=1;i<=n;++i) a[i]=find(a[i]),b[i]=find(b[i]); int m=rst.size(); for (i=1;i<=m;++i) fa[i]=i; vector <int> key; for (i=1;i<=n;++i) if (w[i]==1) addedge(a[i],b[i],i),fa[getfa(a[i])]=getfa(b[i]),key.push_back(a[i]),key.push_back(b[i]); for (i=1;i<key.size();++i) if (getfa(key[0])!=getfa(key[i])) return puts("No"),0; int cnt_s=0,cnt_t=0,s,t; for (i=1;i<=m;++i) { if (abs(in[i]-out[i])>1) return puts("No"),0; if (in[i]+1==out[i]) ++cnt_s,s=i; if (out[i]+1==in[i]) ++cnt_t,t=i; } if ((cnt_s==0&&cnt_t==0)||(cnt_s==1&&cnt_t==1&&s==c)) { DFS(c); reverse(path.begin(),path.end()); for (i=1;i<=n;++i) if (w[i]==1&&!vis[i]) return puts("No"),0; int ots=0; for (i=1;i<=n;++i) if (w[i]==1&&b[i]!=c) ots=i; vector <int> ans; for (i=1;i<=n;++i) if (w[i]==0&&a[i]!=c) ans.push_back(i),vis[i]=1; for (auto cur:path) { ans.push_back(cur); if (cur==ots) { for (i=1;i<=n;++i) if (!vis[i]) ans.push_back(i),vis[i]=1; } } for (i=1;i<=n;++i) if (!vis[i]) return puts("No"),0; puts("Yes"); for (auto x:ans) printf("%d ",x); } else puts("No"); return 0; }

7|0F. Futures Market Trends


数据范围不大,可以直接确定左端点后扩展右端点,考虑每次快速维护A,D的变化

比赛的时候徐神写了分别维护二次项的和、一次项的和的做法,后面发现其实直接用方差等于平方的期望减去期望的平方来做更简单

#include <bits/stdc++.h> using llsi = long long signed int; llsi d, c[3003], x[3003]; long double P; int main() { std::ios::sync_with_stdio(false); std::cin >> d >> P; for(int i = 1; i <= d; ++i) std::cin >> c[i]; c[0] = 0; for(int i = 1; i <= d; ++i) x[i] = c[i] - c[i - 1]; int pos = 0, neg = 0; for(int i = 1; i <= d; ++i) { llsi sx = 0, sx2 = 0; for(int j = i + 1; j <= d; ++j) { sx2 += x[j] * x[j]; sx += x[j]; int n = j - i; if(n >= 2 && (long double)sx * sx >= P * P * (long double)(n * sx2 - sx * sx)) { if(sx > 0) pos += 1; if(sx < 0) neg += 1; } } } std::cout << pos << ' ' << neg << std::endl; return 0; }

8|0G. Grammar Path


题目看着都是如此的毒瘤


9|0H. Heroes of Coin Flipping


祁神想了挺久的这个题,搞了个虚树+大讨论的做法,后面也没写过不知道正确性(主要题解也看不懂是俄语的)

Upt:好好好伟大的祁神力切了此题,看来今天只有我在摆

#include<bits/stdc++.h> using namespace std; #define int long long #define LD double using pii = pair<int, int>; #define ft first #define sd second int n, k; unordered_set<int> st, st2; LD f[2][35]; LD ans=0.0L; int getid(int s, int m){return (1LL<<(k-s))+m-1;} void dfs(int s, int m, int dis, int rt){ if (0==s) return; // printf("dfs(s=%lld m=%lld len=%lld dis=%lld rt=%lld)\n", s, m, len, dis, rt); if (st.count(getid(s, m))){ dfs(s-1, 2*m-1, 0, 0); dfs(s-1, 2*m, 0, 0); }else if (st2.count(getid(s, m))){ ans += f[rt][dis]; // printf("ans=%Lf\n", ans); dfs(s-1, 2*m-1, dis+1, rt); dfs(s-1, 2*m, dis+1, rt); }else{ int len=1; while (s>0){ ans += len*f[rt][dis]; --s; len*=2; ++dis; } // printf("ans=%Lf\n", ans); // dfs(s-1, 2*m-1, len*2, dis+1, rt); } } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout << setiosflags(ios::fixed) << setprecision(15); cin >> k >> n; //f[0/1][x]表示当前点到距离最近的 前n个点/根节点 的距离为x时的贡献 //f[0][x] = $\sum_{i=1}^{x} \frac{1}{(i+1)2^{i}}$ //f[1][x]只有最后与f[0][x]不同 f[0][0] = 0.0L; f[1][0] = 1.0L; int p=1; for (int i=1; i<=k; ++i){ f[1][i] = f[0][i-1] + 1.0L/p/(i+1); p*=2; f[0][i] = f[0][i-1] + 1.0L/p/(i+1); // printf("i=%lld f[0]=%Lf f[1]=%Lf\n", i, f[0][i], f[1][i]); } if (0==n){ int p=1; for (int i=0; i<k; ++i){ ans += p*f[1][i]; p*=2; } cout << ans << '\n'; return 0; } for (int i=1; i<=n; ++i){ int a, b; cin >> a >> b; int x=getid(a, b); st.insert(x); ++a, b=(b+1)/2; int fa=getid(a, b); int p=1; while (a<=k && !st.count(fa)){ p*=2; ++a, b=(b+1)/2; fa=getid(a, b); } if (a>k) ans += 1.0L; else ans += 1.0L - (1.0L/p); // printf("i=%lld ans=%Lf\n", i, ans); } for (auto x : st){ while (x>0 && !st2.count(x)){ st2.insert(x); x /= 2; } } dfs(k, 1, 0, 1); cout << ans << '\n'; return 0; }

10|0I. Integer Square


签到题,直接枚举即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int s; int main() { scanf("%d",&s); for (RI i=0;i*i<=s;++i) for (RI j=0;j*j<=s;++j) if (i*i+j*j==s) { printf("%d %d\n",0,0); printf("%d %d\n",i,j); printf("%d %d\n",-j,i); printf("%d %d\n",i-j,i+j); return 0; } return puts("Impossible"),0; }

11|0J. Joint Password Storage


题目都哈人的一笔,鉴定为不可做题


12|0K. Keys and Locks Boolean Logic


数电题目来了,祁神后面提出一种通用做法,先找出最简的或与式,然后手玩一下可以找到怎么构造与逻辑和或逻辑

但感觉不知道能不能在限制内做出来,同时这题的输入输出感觉都要写大模拟,做不了一点


13|0L. Lost Permutation


ORZ群论大师徐神大力秒了此题,不然我们队后面两个半小时就只能玩手机了

考虑令f=[1,2,3,,n],最后得到的结果一定也是[1,2,3,n]

那么如果我们交换f中的两项会得到什么呢(假设交换了x,y),手玩一下会发现最后会得到一个只有两个数没有还原的排列

并且我们惊喜地发现这个序列中没有还原的两个数恰好是π(x),π(y)上的数

那么我们很容易想到一个naive的做法,例如固定x,问出(x,y1),(x,y2),找到它们返回的没有还原的数中的众数就是π(x)的值

但现在的问题是我们只能问两次,上面的做法看似完全不可行

不过徐神发现可以只问[2,1,3,4,,n1,n](只交换前两个数)和[1,3,4,5,,n,2](固定第一个数,其余的数循环移位)这两个排列,用它们构造出所有的询问情况

(因为询问的信息可以相乘再利用,比如询问f1,f2得到的结果为g1,g2,那么询问f1f2的结果就是g1g2

总复杂度O(n2),实现起来需要一定技巧和细节

#include <bits/stdc++.h> struct pm: public std::vector<int> { inline pm(size_t n): vector(n) {} friend pm operator*(const pm &a, const pm &b) { pm c(a.size()); for(int i = 0; i < a.size(); ++i) c[i] = b[a[i]]; return c; } }; void work() { int n; std::cin >> n; std::cout << "? 2 1"; for(int i = 3; i <= n; ++i) std::cout << ' ' << i; std::cout << std::endl; pm a(n); for(auto &a: a) std::cin >> a, --a; std::cout << "? 1"; for(int i = 3; i <= n; ++i) std::cout << ' ' << i; std::cout << " 2" << std::endl; pm b(n), bi(n), bs(n), be(n); for(auto &b: b) std::cin >> b, --b; for(int i = 0; i < n; ++i) bi[b[i]] = i; for(int i = 0; i < n; ++i) be[i] = bs[i] = i; for(int i = 0; i < n - 1; ++i) be = b * be; std::vector<std::vector<int>> sns(n, std::vector<int>()); for(int i = 1; i < n; ++i) { int pi = i; pm ars(n); for(int j = 0; j < n; ++j) ars[j] = j; ars = be * a * bs * ars; // std::cerr << "debug ars:"; // for(int j = 0; j < n; ++j) std::cerr << ars[j] + 1 << char(j == n - 1 ? 10 : 32); for(int j = 0; j < n; ++j) if(ars[j] != j) { sns[0].push_back(j); sns[i].push_back(j); } bs = b * bs; be = bi * be; } std::sort(sns[0].begin(), sns[0].end()); int a0, a0c = 0; { int l = 0, r = 0; while(l < sns[0].size()) { while(r < sns[0].size() && sns[0][r] == sns[0][l]) r += 1; if(r - l > a0c) { a0 = sns[0][l]; a0c = r - l; } l = r; } } std::cout << "! " << a0 + 1; for(int i = 1; i < n; ++i) for(auto sns: sns[i]) if(sns != a0) std::cout << ' ' << sns + 1; std::cout << std::endl; return ; } int main(void) { std::ios::sync_with_stdio(false); int t; std::cin >> t; while(t--) work(); return 0; }

14|0M. Mind the Gap


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

#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; int n, A[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n; A[0]=0; for (int i=1; i<=n; ++i) cin >> A[i]; sort(A+1, A+n+1); int L=1, R=(int)1e9; int ans=0; while (L<R){ int M = L+(R-L)/2; bool f1=false, f2=false; for (int i=0; i<n; ++i) if (A[i+1]-A[i]>M) f1=true; for (int i=0; i<n-1; ++i) if (A[i+2]-A[i]<=M) f2=true; if (f1 && f2) break; else if (f1) L=M+1; else if (f2) R=M; else{ans=M; break;} } cout << ans << '\n'; return 0; }

15|0N. Nunchucks Shop


我只看了个题意就被祁神秒了

枚举在一边的珠子的个数,根据奇偶性讨论一下中心对称的方案数

#include<bits/stdc++.h> #define int long long using namespace std; const int N = 55; int C[N][N]; int n, k; signed main(){ ios::sync_with_stdio(0); cin.tie(0); C[1][0]=C[1][1]=C[0][0]=1; for (int i=2; i<N; ++i){ C[i][0]=C[i][i]=1; for (int j=1; j<i; ++j){ C[i][j]=C[i-1][j-1]+C[i-1][j]; // printf("C(%lld %lld)=%lld\n", i, j, C[i][j]); } } cin >> n >> k; int ans=0; for (int x=0; x<=n; ++x){ if (k-x<0 || k-x>n) continue; int res=0; if (n%2==0 && x%2==1) res += C[n][x]/2; else res += (C[n][x]-C[n/2][x/2])/2+C[n/2][x/2]; if (k%2==0 && x==k/2) res*=2; ans += res; // printf("x=%lld res=%lld\n", x, res); } cout << ans << '\n'; return 0; }

16|0Postscript


这场题感觉没啥区分度,前面的题很简单后面的题又极难,而且感觉考的都比较诡异,和国内的风格不太像说是


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17985718.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示