隐藏页面特效

2019-2020 ICPC Northwestern European Regional Programming Contest (NWERC 2019)

1|0Preface


由于前两天疑似感染风寒,今天头痛+鼻塞+咽炎一条龙,硬打了4h顶不住就下班了

最后过了8个题也还行,比较可惜的就是H题从中期写到结束,祁神和徐神各写了一个version也没过


2|0A. Average Rank


挺有意思的一个题

考虑将一个原来分数为x的人加1分后会发生什么,显然只有原来分数也为x分的人的排名往后移了一位

直接暴力维护是O(nw)的,但我们仔细一想可以记录一下每个分数上一次更新的时间,以此略过不受影响的排名

#include<cstdio> #include<iostream> #define int long long #define RI register int #define CI const int& using namespace std; const int N=300005; int n,w,x,y,p[N],rk[N],lst[N],sum[N],coef[N],pre[N]; signed main() { RI i,j; for (scanf("%lld%lld",&n,&w),i=0;i<=w;++i) lst[i]=1; for (i=1;i<=w;++i) { for (scanf("%lld",&x),j=1;j<=x;++j) { scanf("%lld",&y); coef[p[y]]+=rk[p[y]]*(i-lst[p[y]]); lst[p[y]]=i; ++rk[p[y]]; sum[y]+=coef[p[y]]-pre[y]; ++p[y]; coef[p[y]]+=rk[p[y]]*(i-lst[p[y]]); lst[p[y]]=i; pre[y]=coef[p[y]]; } } for (i=1;i<=n;++i) { coef[p[i]]+=rk[p[i]]*(w+1-lst[p[i]]); lst[p[i]]=w+1; sum[i]+=coef[p[i]]-pre[i]; printf("%.9lf\n",(1.0*sum[i]/w+1.0)); } return 0; }

3|0B. Balanced Cut


应该是本场的防AK题,做不来一点


4|0C. Canvas Line


徐神写的,我题目都没看

#include <bits/stdc++.h> int n, p; std::map<int, std::pair<int, int>> canvas; std::set<int> pegs, at; using mit = std::map<int, std::pair<int, int>>::iterator; mit get_next(mit it) { if(it == canvas.end()) return it; int r = it->second.first; ++it; if(it == canvas.end()) return it; return it->first == r ? it : canvas.end(); } bool try_put(int x, bool ne = false) { if(pegs.find(x) != pegs.end()) return false; auto it = canvas.lower_bound(x); if(x == it->first) { if(it->second.second == 2) return false; if(it != canvas.begin()) { auto pre = it; --pre; if(pre->second.first == x) { if(pre->second.second == 2) return false; pre->second.second += 1; ne = true; } } it->second.second += 1; ne = true; } else if(it != canvas.begin()) { --it; if(it->first <= x && x <= it->second.first) { if(it->second.second == 2) return false; it->second.second += 1; ne = true; } } if(ne) pegs.insert(x); return true; } int main(void) { std::ios::sync_with_stdio(false); std::cin >> n; for(int i = 0, l, r; i < n; ++i) { std::cin >> l >> r; canvas[l] = {r, 0}; } std::cin >> p; for(int i = 0, x; i < p; ++i) { std::cin >> x; at.insert(x); if(!try_put(x, true)) return std::cout << "impossible\n", 0; } for(auto &[l, val]: canvas) { auto &[r, count] = val; if(count > 2) return std::cout << "impossible\n", 0; int x = r; while(count < 2 && x >= l) try_put(x--); if(x < l) return std::cout << "impossible\n", 0; } std::vector<int> pgs; for(auto p: pegs) if(at.find(p) == at.end()) pgs.push_back(p); std::cout << pgs.size() << char(10); for(int i = 0; i < pgs.size(); ++i) std::cout << pgs[i] << char(i == pgs.size() - 1 ? 10 : 32); return 0; }

5|0D. Disposable Switches


挺有意思的一个题

首先可以发现题目中给出的两个参数一起可以统一为一个,即将所有边权整体乘上v后得到l+cv,将cv整体看作一个变量

注意到cv的贡献之和所经过的边数有关,因此考虑在跑最短路的时候把这个信息保留下来

不妨设fi,j表示从1号点走到i号点,恰好经过j条边的最短路,这个随便DP一下就能求出

假设最后某条1n的最短路恰经过了k条边,将cv看作自变量xfn,k看作截距,则可以得到一条直线y=kx+fn,k

不难发现将所有的直线画出来后最后可能称为最短路的就是一个半平面交构成的区域

找出所有在半平面上的直线,倒着还原一遍DP过程即可找出所有的关键点

总复杂度O(nm)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #define int long long #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=2005,INF=1e18; int n,m,x,y,z,f[N][N],key[N],vis[N][N]; vector <pi> v[N]; inline int Cross(const pi& A,const pi& B,const pi& C) { return (B.fi-A.fi)*(C.se-A.se)-(C.fi-A.fi)*(B.se-A.se); } inline void travel(CI now,CI len,CI dis) { if (vis[now][len]) return; vis[now][len]=1; key[now]=1; for (auto [pre,w]:v[now]) if (f[pre][len-1]+w==dis) travel(pre,len-1,f[pre][len-1]); } signed main() { RI i,j; for (scanf("%lld%lld",&n,&m),i=1;i<=m;++i) scanf("%lld%lld%lld",&x,&y,&z),v[x].push_back(pi(y,z)),v[y].push_back(pi(x,z)); for (i=1;i<=n;++i) for (j=0;j<=n;++j) f[i][j]=INF; for (f[1][0]=0,j=0;j<n;++j) for (i=1;i<=n;++i) if (f[i][j]!=INF) for (auto [to,w]:v[i]) f[to][j+1]=min(f[to][j+1],f[i][j]+w); vector <pi> stk; for (i=n;i>=1;--i) if (f[n][i]!=INF) { pi cur=pi(i,f[n][i]); while (stk.size()>=2&&Cross(stk[stk.size()-2],stk.back(),cur)>0) stk.pop_back(); if (stk.size()==1&&stk.back().se>cur.se) stk.pop_back(); stk.push_back(cur); } for (auto [len,dis]:stk) travel(n,len,dis); vector <int> ans; for (i=1;i<=n;++i) if (!key[i]) ans.push_back(i); printf("%lld\n",ans.size()); for (auto x:ans) printf("%lld ",x); return 0; }

6|0E. Expeditious Cubing


徐神开场写的,WA了两发,看来有不少坑点

#include <bits/stdc++.h> int rd() { long double s; std::cin >> s; return std::round(s * 100.L); } int main() { std::vector<int> a(4); for(int &a: a) a = rd(); std::sort(a.begin(), a.end()); int b = rd() * 3; // if(a[0] + a[1] + a[2] >= b) return std::cout << "infinite\n", 0; // if(a[1] + a[2] + a[3] < b) return std::cout << "impossible\n", 0; int ans = a[0] - 1; for(int i = a[3] + 1; i >= a[0]; --i) { std::vector<int> c = a; c.push_back(i); std::sort(c.begin(), c.end()); if(c[1] + c[2] + c[3] <= b) { ans = i; break; } } if(ans == a[0] - 1) return std::cout << "impossible\n", 0; if(ans == a[3] + 1) return std::cout << "infinite\n", 0; std::cout << ans / 100 << "." << (ans % 100 < 10 ? "0" : "") << ans % 100 << char(10); return 0; }

7|0F. Firetrucks Are Red


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

#include<bits/stdc++.h> using namespace std; const int N = 2e5+5; int n, fa[N]; int gf(int x){return x==fa[x] ? x : fa[x]=gf(fa[x]);} map<int, int> mp; int idx=0; vector<int> G[N]; struct Node{ int a, b, p; }; vector<Node> ans; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n; idx=0; for (int i=1; i<=n; ++i) fa[i]=i; for (int i=1; i<=n; ++i){ int m; cin >> m; for (int j=1; j<=m; ++j){ int a; cin >> a; if (!mp.count(a)) mp[a]=++idx; G[mp[a]].push_back(i); } } for (auto [x, id] : mp){ int sz=G[id].size(); for (int j=1; j<sz; ++j){ if (gf(G[id][j])!=gf(G[id][0])){ ans.push_back(Node{G[id][j], G[id][0], x}); fa[gf(G[id][j])] = gf(G[id][0]); } } } bool ok=true; for (int i=2; i<=n; ++i) if (gf(i)!=gf(1)){ok=false; break;} if (!ok) cout << "impossible\n"; else{ for (auto [a, b, p] : ans){ cout << a << ' ' << b << ' ' << p << '\n'; } } return 0; }

8|0G. Gnoll Hypothesis


这题的题意比较鬼畜,我刚开始看错了好几个版本的题意,后面让祁神再读了一遍才搞懂

考虑枚举圆环上两个相邻的选中位置x,y(x<y),此时sy=i=x+1ysi,这种局面出现的概率就是Cn(yx+1)k2Cnk

注意特判k=1的情形

#include<bits/stdc++.h> using namespace std; using LD = long double; const int N = 505; int n, k; LD sum[N][N]; LD C[N][N]; LD ans[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout << setiosflags(ios::fixed) << setprecision(10); cin >> n >> k; if (1==k){ for (int i=0; i<n; ++i) cout << 1.0L/n*100 << ' '; cout << '\n'; return 0; } C[0][0] = C[1][0] = C[1][1] = 1.0L; for (int i=2; i<=n; ++i){ C[i][0] = C[i][i] = 1.0L; for (int j=1; j<i; ++j) C[i][j] = C[i-1][j-1] + C[i-1][j]; } for (int i=0; i<n; ++i) cin >> sum[i][0]; for (int j=1; j<n-1; ++j){ for (int i=0; i<n; ++i){ sum[i][j] = sum[i][j-1]+sum[(i+n-j)%n][0]; } } for (int i=0; i<n; ++i){ for (int x=0; x<=n-k; ++x){ ans[i] += C[n-x-2][k-2] / C[n][k] * sum[i][x]; } } for (int i=0; i<n; ++i) cout << ans[i] << ' '; cout << '\n'; return 0; }

9|0H. Height Profile


这题看数据范围就是允许用O(nklogn)的做法的,因此我们考虑对于每个询问分开处理

对于两点(x1,y1),(x2,y2)(x1<x2),需要满足y2y1x2x1g,移项后得到y2gx2y1gx1

不妨设y=ygx,则原问题等价于找出一段最长的区间[l,r]使得yryl0

然后祁神和徐神写了两个version的扫描线,都不知道挂在哪里了,坐等队友补题了


10|0I. Inverted Deck


考虑令b为排序后的a数组,原问题等价于要翻转b的某段区间使其等于a

找到a,b两个数组不相同位置的左右端点,翻转这个区间后检验是否相同即可

#include<bits/stdc++.h> using namespace std; const int N = 1e6+5; int n, A[N], B[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n; for (int i=1; i<=n; ++i){ int a; cin >> a; A[i] = B[i] = a; } sort(B+1, B+n+1); int L=-1, R=-1; for (int i=1; i<=n; ++i){ if (A[i]!=B[i]){ if (-1==L) L=i; R=i; } } if (-1==L) cout << "1 1\n"; else{ bool ok=true; for (int i=L; i<=R; ++i){ if (A[i] != B[L+R-i]){ok=false; break;} } if (!ok) cout << "impossible\n"; else cout << L << ' ' << R << '\n'; } return 0; }

11|0J. Jackdaws And Crows


很有意思的一个题

考虑如果我们确定了建小号的次数为x,怎么求此时需要删除的帖子数的最小值

不难发现此时可以把所有数分为三类:

  • 无论怎么操作都为正数,记为+
  • 无论怎么操作都为负数,记为-
  • 可以人为操控其正负性,记为?

而对于一段由上述三种字符构成的序列,要求其最少需要删除多少个字符,使得将所有的?赋值后得到一个加减交错的序列

手玩归纳一下会发现一个很好的性质,对于两个相邻的+-,设它们中间有t?(可以为0个),当

  • 相邻的两个符号不同且t是奇数时,需要删除一个字符
  • 相邻的两个符号相同且t是偶数时,需要删除一个字符

刚开始想了一堆繁琐的方法来维护这个东西,后面意识到可以从小到大枚举x,此时的操作只有+-变成?

拿一个链表去维护序列中所有的+-,代码十分好写

#include<cstdio> #include<iostream> #include<algorithm> #include<utility> #define int long long #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=500005; int n,c,r,s[N],L[N],R[N],ans,ret; pi p[N]; inline int sgn(CI x) { if (x>0) return 1; if (x<0) return -1; return 0; } inline void remove(CI x) { R[L[x]]=R[x]; L[R[x]]=L[x]; } inline int calc(CI x,CI y) { if (x<1||x>n||y<1||y>n) return 0; if (sgn(s[x])!=sgn(s[y])&&(y-x-1)%2==1) return r; if (sgn(s[x])==sgn(s[y])&&(y-x-1)%2==0) return r; return 0; } signed main() { RI i; for (scanf("%lld%lld%lld",&n,&c,&r),i=1;i<=n;++i) scanf("%lld",&s[i]),p[i]=pi(abs(s[i]),i); int lst=0; for (i=1;i<=n;++i) { if (!s[i]) { ans+=r; continue; } if (lst&&sgn(s[lst])==sgn(s[i])) ans+=r; lst=i; } for (i=1;i<=n;++i) L[i]=i-1,R[i]=i+1; R[0]=1; L[n+1]=n; for (lst=0,i=1;i<=n;++i) if (!s[i]) remove(i); else ret+=calc(lst,i),lst=i; sort(p+1,p+n+1); ans=min(ans,c+ret); for (i=1;i<=n;++i) if (p[i].fi) { auto [val,pos]=p[i]; ret-=calc(L[pos],pos)+calc(pos,R[pos]); ret+=calc(L[pos],R[pos]); remove(pos); ans=min(ans,c*(val+1)+ret); } return printf("%lld",ans),0; }

12|0K. Kitesurfing


又是防AK题,做不来一点


13|0Postscript


今天状态确实不太好,估计明天要稍微休息一下了的说


__EOF__

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