2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest AGHIJM 题解
A. LaIS
设 \(dp_i\) 为到第 i 位的最长的 almost increasing 长度。可以发现,这个 \(dp_i\) 的转移只有从 \(a_j \leq a_i\) 的地方转移过去,或者是找到一个最靠后的 k 使得 \(a_k > a_i\),再从 \([1,k-1]\) 找到一个 j 满足 \(a_j \leq a_i\)。
那么,递推式为
\begin{cases}
\max\{dp_j\} + 1, &a_j \leq a_i, j \in [1, i-1]
\\
\max\{dp_j\}+2, &a_j \leq a_i, j \in [1, k-1]
\end{cases}
\]
那么可以用一个单调递减的队列来维护并找出 k。用主席树来维护 \(dp\) 数组,下标 \(a_i\) 存值 \(dp_i\)。时间复杂度 \(O(n \log n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef long double ld;
#define IL inline
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#define dbg1(x) cout << #x << " = " << x << ", "
#define dbg2(x) cout << #x << " = " << x << endl
template<typename Tp> IL void read(Tp &x) {
x=0; int f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
x *= f;
}
int buf[42];
template<typename Tp> IL void write(Tp x) {
int p = 0;
if(x < 0) { putchar('-'); x=-x;}
if(x == 0) { putchar('0'); return;}
while(x) {
buf[++p] = x % 10;
x /= 10;
}
for(int i=p;i;i--) putchar('0' + buf[i]);
}
const int N = 500000 + 5;
struct PRETree {
int n, tot;
int rt[N];
int maxv[N << 5], ls[N << 5], rs[N << 5];
void init(int n) {
this -> n = n;
fill(rt, rt + n + 1, 0);
fill(maxv, maxv + (n << 5) + 1, 0);
fill(ls, ls + (n << 5) + 1, 0);
fill(rs, rs + (n << 5) + 1, 0);
}
int newnode() { return ++tot;}
void copynode(int u, int v) {
maxv[u] = maxv[v];
ls[u] = ls[v];
rs[u] = rs[v];
}
int upd(int pre, int L, int R, int pos, int v) {
int o = newnode();
copynode(o, pre);
if(L == R) {
maxv[o] = v;
return o;
}
int M = L+R >> 1;
if(pos <= M) ls[o] = upd(ls[pre], L, M, pos, v);
else rs[o] = upd(rs[pre], M+1, R, pos, v);
maxv[o] = max(maxv[ls[o]], maxv[rs[o]]);
return o;
}
int query(int o, int L, int R, int qL, int qR) {
if(qL <= L && R <= qR) {
return maxv[o];
}
int M = L+R >> 1;
int ans = 0;
if(qL <= M) ans = max(ans, query(ls[o], L, M, qL, qR));
if(M < qR) ans = max(ans, query(rs[o], M+1, R, qL, qR));
return ans;
}
int query_max(int posR, int aR) {
if(posR == 0) return 0;
return query(rt[posR], 1, n, 1, aR);
}
}lpr;
int n;
int a[N], dp[N];
int m;
int q[N];
int main() {
#ifdef LOCAL
freopen("A.in", "r", stdin);
#endif
int T; read(T);
while(T--) {
read(n);
lpr.init(n);
m = 0;
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) {
dp[i] = lpr.query_max(i-1, a[i]) + 1;
if(m >= 1) {
int pos = lower_bound(q+1, q+m+1, i, [&](int x, int y) { return a[x] > a[y];}) - q - 1;
if(pos > 0) {
dp[i] = max(dp[i], lpr.query_max(q[pos]-1, a[i]) + 2);
q[m = pos + 1] = i;
}
else q[m = 1] = i;
}
else {
q[++m] = i;
}
lpr.rt[i] = lpr.upd(lpr.rt[i-1], 1, n, a[i], dp[i]);
}
int ans = 0;
// puts("dp[i] = ");
// for(int i=1;i<=n;i++) {
// dbg1(i); dbg2(dp[i]);
// }
for(int i=1;i<=n;i++) ans = max(ans, dp[i]);
printf("%d\n", ans);
}
return 0;
}
G. Hobbits
非常简单的计算几何,模拟的时候为啥没读到?
维护一个最高的点, 然后判断线段是不是在眼睛到最高点这个向量左侧即可。记得判一下平行。
eps 开太小了导致本机能过的数据交上去 WA 了。
#include <bits/stdc++.h>
using namespace std;
typedef double db;
#define IL inline
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#define dbg1(x) cout << #x << " = " << x << ", "
#define dbg2(x) cout << #x << " = " << x << endl
const db eps = 1e-8;
int dcmp(db x) {
if(fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}
struct P {
db x, y;
P(db x=0.0, db y=0.0):x(x), y(y) {}
};
typedef P V;
IL V operator + (const V& a, const V& b) { return V(a.x + b.x, a.y + b.y);}
IL V operator - (const V& a, const V& b) { return V(a.x - b.x, a.y - b.y);}
IL V operator * (const V& a, db p) { return V(a.x * p, a.y * p);}
IL V operator / (const V& a, db p) { return V(a.x / p, a.y / p);}
IL bool operator < (const P& a, const P& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
IL bool operator == (const P& a, const P& b) {
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
IL db Dot(const V& a, const V& b) { return a.x * b.x + a.y * b.y;}
IL db Length(const V& a) { return sqrt(Dot(a, a));}
IL db Angle(const V& a, const V& b) { return acos(Dot(a, b) / Length(a) / Length(b));}
IL db Cross(const V& a, const V& b) { return a.x*b.y - a.y*b.x;}
IL P GetLineIntersection(P p, V v, P q, V w) {
V u = p-q;
db t = Cross(w, u) / Cross(v, w);
return p + v * t;
}
IL bool OnLeft(P a, P b, P p) {
return dcmp(Cross(b - a, p - a)) > 0;
}
IL bool OnSegment(P p, P a1, P a2) {
return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;
}
const int N = 200000 + 5;
int n, H;
P p[N];
int main() {
#ifdef LOCAL
freopen("G.in", "r", stdin);
#endif
scanf("%d%d", &n, &H);
for(int i=1;i<=n;i++) {
int xx, yy; scanf("%d%d", &xx ,&yy);
p[i] = P(xx, yy);
}
p[0] = P(p[n].x, p[n].y + H);
P high = p[n];
db ans = 0.0;
for(int i=n;i>=2;i--) {
if(!OnLeft(p[0], high, p[i])) high = p[i];
V v = p[i-1] - p[i];
if(dcmp(Cross(v, high - p[0])) == 0) { // parallel
if(OnLeft(p[0], high, p[i])) continue;
else ans += Length(v);
continue;
}
// not parallel
int b = 2;
P a = GetLineIntersection(p[0], high - p[0], p[i], p[i-1] - p[i]);
if(OnSegment(a, p[i-1], p[i])) {
ans += Length(a - p[i-1]);
}
else {
if(dcmp(Cross(high - p[0], p[i] - p[0])) >= 0 && dcmp(Cross(high - p[0], p[i-1] - p[0])) >= 0) continue;
else ans += Length(p[i] - p[i-1]);
}
}
printf("%.10lf\n", ans);
return 0;
}
H. K and Medians
结论是满足以下两个条件就是 yes。
- \((k-1) | (n-m)\)
- 删后的序列中满足一个条件,存在某一个数在删后的序列中且它前面删了 \(\frac{k-1}{2}\) 个数,后面删了 \(\frac{k-1}{2}\) 个数。
以上。
#include <bits/stdc++.h>
using namespace std;
const int N = 200000 + 5;
int n, k, m;
int a[N];
bool check() {
for(int i=1;i<=m;i++) {
int delL = a[i] - i;
int delR = (n - a[i]) - (m - i);
if(delL >= (k-1) / 2 && delR >= (k-1) / 2) return true;
}
return false;
}
int main() {
int T; scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &k, &m);
for(int i=1;i<=m;i++) scanf("%d", a+i);
if((n-m) % (k-1) != 0) { puts("NO"); continue;}
puts(check() ? "YES" : "NO");
}
return 0;
}
I. Plane Tiling
题意:给你两个向量 \(\vec{v_1} = (dx_1, dy_1), \vec{v_2} = (dx_2, dy_2)\) 请恰好选 \(n\) 个不同点 \(p_i = (x_i, y_i)\),使得平面上的所有点都能由下式表示出来。\(P = p_i + a \cdot \vec{v_1} + b \cdot \vec{v_2}\)
做法:先把从 \((0, 0)\) 出发的向量 \(\vec{v_1}, \vec{v_2}\) 构成的平行四边形画出来。接着扩展这个平行四边形,铺满整个平面(可以自行脑补长边贴着长边,短边贴着短边)。然后可以发现整个平面很多点在平行四边形的顶点上。答案其实就等于单个平行四边形内不在平行四边形顶点的点的个数。尝试计算它,可以发现
不在平行四边形顶点上的点的个数 = 平行四边形内部的点的个数 + (平行四边形边上的点的个数 - 4) / 2(注:这里 /2 是因为计算答案的时候只能计算左边和上边。右边和下边会和平面上别的平行四边形算重。减去的是顶点)
引用 pick 定理,可以发现 答案 = 平行四边形面积 - 1 + 1 = 平行四边形面积。(注:这里 +1 是考虑 \((0,0)\) )
所以第一步先判断 \(S = |dx_1 \cdot dy_2 - dx_2 \cdot dy_1|\) 是否等于 \(n\)。如果不等于则无解,反之有解。
法一:接下来可以把平行四边形里面的所有点都输出出来,即平行四边形内部的点和位于上边、左边的点并去掉顶点。记得输出 $(0, 0) $。
法二:再试试分割 \(dx_1, dx_2, dy_1, dy_2\)。设 \(d_x = \gcd (dx_1, dx_2), d_y = \gcd(dy_1, dy_2), m = n / (d_x \cdot d_y)\),那么最后的答案 \(n = m \cdot d_x \cdot d_y\)。
先取 \(x \in [0, d_x), y \in [0, d_y)\) 中的所有点,很显然它们每一个都无法相互表示出来。接下来考虑如何用 \(m\) 去扩展这些点。这一步可以把选出来的点矩阵沿着 \(x\) 轴正向每次平移 \(d_x\) 格共平移 \(m-1\) 次。这个即为答案。
证明:由于 \(a \cdot dy_1 + b \cdot dy_2\) 无法构造出一个 \(y\) 使得 \(0 < y < d_y\),那么我们考虑在 \(a \cdot dy_1 + b \cdot dy_2=0\) 的时候,是否会使得 \(x\) 重选或者漏选。由上式可得 \(a = k \cdot dy_2 / d_y, b = -k \cdot dy_1 / d_y, k \in Z\)。带入 \(x\) 相关式子得
\]
而 \(S / d_y = n / d_y = m \cdot d_x\),确实是平移距离。
综上所述,证毕。
最终答案即为 \((i \cdot d_x + r_x, r_y), i \in [0,m), r_x \in [0, d_x), r_y \in [0, d_y)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IL inline
long long n;
long long dx1, dx2, dy1, dy2;
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b);}
IL ll absl(ll x) { return x < 0 ? -x : x;}
int main() {
#ifdef LOCAL
freopen("I.in", "r", stdin);
#endif
scanf("%lld", &n);
scanf("%lld%lld", &dx1, &dy1);
scanf("%lld%lld", &dx2, &dy2);
long long d = absl(dx1 * dy2 - dx2 * dy1);
if(d != n) return puts("NO"), 0;
ll dx = absl(gcd(dx1, dx2)), dy = absl(gcd(dy1, dy2));
ll m = n / dx / dy;
puts("YES");
for(int rx=0;rx<dx;rx++) {
for(int ry=0;ry<dy;ry++) {
for(int i=0;i<m;i++) {
printf("%lld %lld\n", i*dx+rx, 1ll * ry);
}
}
}
return 0;
}
J. Road Reform
先做一次最小生成树,得到最小生成树 T。设做完后最大边权为 \(v\)。
- 若 \(v \geq k\),那么由于最小生成树又是最小瓶颈树,那么 \(ans = \sum_{e \in T} (v_e - k)[v_e > k]\)
- 若 \(v < k\),那么从非最小生成树边与最小生成树最大边权 \(v\) 中找到离 k 最近的边。
不知道模拟的时候自己在干什么,可能在梦游?
M. Similar Sets
题意:求出二分图中是否存在四元环。若存在,输出四元环在左边集合中的两个点的下标。左边集合大小 \(n \leq 10^5\),右边集合大小 \(m \leq 2 \cdot 10^5\),最坏情况下可能会有 \(2 \cdot 10^5\) 条边。
做法:第一步离散化。再考虑分治。设左边集合中度数大于等于 \(D\) 的点为大点,其余点为小点。枚举每一个左边集合中的点 \(i\),对大点做一种操作,对小点做另一种操作。
- 若该点 \(i\) 度数为 \(A\) 大于等于 \(D\),我们看这个点与其他左边集合中其他的点的关系。再设一个 \(w_x\) 数组,若右边集合中下标为 \(x\) 的点与点 \(i\) 相连,这个 \(w_x = 1\),反之 \(w_x = 0\)。这样每次枚举到一个大点,把这个点对应的 \(w_x\) 数组弄出来,然后对左边集合中剩余其他点判断是否有两个 \(w_x\) 为 \(1\)。如果题目想要数据一直不匹配到四元环的话,单次的时间复杂度为 \(O(A + m) = O(m)\)。由于如果找到了四元环可以直接跳出,所以如果数据一直找不到四元环的话,左边集合中的每两个大点,最多只会连接重复的一个右边集合中的点。那么最多只会有 \(O(\frac{m}{D})\) 个大点。那么这一步总体的最坏时间复杂度应为 \(O(\frac{m}{D} \cdot m) = O(\frac{m^2}{D})\)
- 若该点 \(i\) 度数为 \(A\) 小于 \(D\),是个小点,由于上一步看过了所有大点和其他所有点的关系,这里看这个点与其他小点的关系即可。接下来我们把点 \(i\) 所连接的所有右边集合中的点组成的点对 \((x,y), x<y\) 推入\(v[x]\) 这个 vector 中。然后枚举完所有小点得到整个 vector 后,再看哪一个 vector 中某个数出现了两次。由于 \(\sum_{i=1}^n A_i = m\) ,时间复杂度应为 \(O(\sum A_i^2[A_i < D])\),根据导数等高中知识,让越多的 \(A_i\) 取到 \(D\) 这个时间复杂度越大,所以最坏时间复杂度为 \(O(D^2 \frac{m}{D}) = O(mD)\)。
最终时间复杂度为 \(O(\frac{m^2}{D} + mD)\),在 \(D = \sqrt{m}\) 时取到较优秀的时间复杂度为 \(O(m \sqrt m)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef long double ld;
typedef pair<int, int> pii;
#define IL inline
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#define dbg1(x) cout << #x << " = " << x << ", "
#define dbg2(x) cout << #x << " = " << x << endl
template<typename Tp> IL void read(Tp &x) {
x=0; int f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
x *= f;
}
int buf[42];
template<typename Tp> IL void write(Tp x) {
int p = 0;
if(x < 0) { putchar('-'); x=-x;}
if(x == 0) { putchar('0'); return;}
while(x) {
buf[++p] = x % 10;
x /= 10;
}
for(int i=p;i;i--) putchar('0' + buf[i]);
}
const int N = 100000 + 5;
const int M = N << 1;
int n, m, mm;
int tmp[M];
int pre[M];
int w[M];
vector<int> a[N];
vector<pair<int, int> > mp[M];
void solve() {
mm = 0;
read(n);
for(int i=1;i<=n;i++) {
a[i].clear();
int ki; read(ki);
for(int j=1;j<=ki;j++) {
int x; read(x);
a[i].push_back(x);
tmp[++mm] = x;
}
sort(a[i].begin(), a[i].end());
}
sort(tmp+1, tmp+1+mm);
m = unique(tmp+1, tmp+1+mm) - tmp - 1;
for(int i=1;i<=n;i++) {
for(int j=0;j<a[i].size();j++) {
a[i][j] = lower_bound(tmp+1, tmp+1+m, a[i][j]) - tmp;
}
}
int sz = sqrt(mm + 0.5);
// big -> others
bool ok = false;
for(int i=1;i<=n;i++) {
if(a[i].size() < sz) continue;
for(int j=0;j<a[i].size();j++) w[a[i][j]] = 1;
for(int j=1;j<=n;j++) if(i != j) {
if(j < i && a[j].size() >= sz) continue;
int cnt = 0;
for(int k=0;k<a[j].size();k++) {
if(w[a[j][k]]) cnt++;
if(cnt >= 2) { printf("%d %d\n", i, j); ok = true; break;}
}
if(ok) break;
}
for(int j=0;j<a[i].size();j++) w[a[i][j]] = 0;
if(ok) return;
}
// small -> small
for(int i=1;i<=n;i++) {
if(a[i].size() >= sz) continue;
for(int j=0;j<a[i].size();j++) {
for(int k=j+1;k<a[i].size();k++) {
mp[a[i][j]].push_back(mk(a[i][k], i));
}
}
}
for(int x=1;x<=m;x++) {
for(auto pp : mp[x]) {
int y = pp.first, p = pp.second;
if(pre[y]) {
printf("%d %d\n", p, pre[y]);
ok = true;
break;
}
pre[y] = p;
}
for(auto pp : mp[x]) {
int y = pp.first, p = pp.second;
pre[y] = 0;
}
if(ok) break;
}
for(int i=1;i<=m;i++) mp[i].clear();
if(ok) return;
puts("-1");
return;
}
int main() {
#ifdef LOCAL
freopen("M.in", "r", stdin);
#endif
int T; read(T);
while(T--) solve();
return 0;
}
2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest AGHIJM 题解的更多相关文章
- 2019-2020 ICPC, NERC, Southern and Volga Russian Regional Contest
目录 Contest Info Solutions A. Berstagram B. The Feast and the Bus C. Trip to Saint Petersburg E. The ...
- 2019-2020 ICPC, NERC, Southern and Volga Russian Regional Contest (Online Mirror, ICPC Rules, Teams Preferred)【A题 类型好题】
A. Berstagram Polycarp recently signed up to a new social network Berstagram. He immediately publish ...
- 2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest (Online Mirror, ICPC Rules) D. Firecrackers (贪心,二分)
题意:有个长度为\(n\)的监狱,犯人在位置\(a\),cop在位置\(b\),你每次可以向左或者向右移动一个单位,或者选择不动并在原地放一个爆竹\(i\),爆竹\(i\)在\(s[i]\)秒后爆炸, ...
- 2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest (Online Mirror, ICPC Rules) C. Berpizza (STL)
题意:酒吧里有两个服务员,每个人每次都只能服务一名客人,服务员2按照客人进酒吧的顺序服务,服务员3按照客人的钱来服务,询问\(q\),\(1\)表示有客人进入酒吧,带着\(m\)块钱,\(2\)表示询 ...
- 2017 ACM - ICPC Asia Ho Chi Minh City Regional Contest
2017 ACM - ICPC Asia Ho Chi Minh City Regional Contest A - Arranging Wine 题目描述:有\(R\)个红箱和\(W\)个白箱,将这 ...
- 2018 ICPC Pacific Northwest Regional Contest I-Inversions 题解
题目链接: 2018 ICPC Pacific Northwest Regional Contest - I-Inversions 题意 给出一个长度为\(n\)的序列,其中的数字介于0-k之间,为0 ...
- 2021 ICPC Gran Premio de Mexico 2da Fecha部分题题解
前面的水题,在队友的配合下,很快就拿下了,剩下几道大毒瘤题,一直罚座三个小时,好让人自闭...但不得不说,这些题的质量是真的高! H. Haunted House 首先看这个题,大眼一扫,觉得是某种数 ...
- 2018-2019 ICPC, NEERC, Southern Subregional Contest
目录 2018-2019 ICPC, NEERC, Southern Subregional Contest (Codeforces 1070) A.Find a Number(BFS) C.Clou ...
- Codeforces 2018-2019 ICPC, NEERC, Southern Subregional Contest
2018-2019 ICPC, NEERC, Southern Subregional Contest 闲谈: 被操哥和男神带飞的一场ACM,第一把做了这么多题,荣幸成为7题队,虽然比赛的时候频频出锅 ...
- ACM ICPC Central Europe Regional Contest 2013 Jagiellonian University Kraków
ACM ICPC Central Europe Regional Contest 2013 Jagiellonian University Kraków Problem A: Rubik’s Rect ...
随机推荐
- 尝试 IIncrementalGenerator 进行增量 Source Generator 生成代码
在加上热重载时,源代码生成 Source Generator 的默认行为会让 Visual Studio 有些为难,其原因是热重载会变更代码,变更代码触发代码生成器更新代码,代码生成器更新的代码说不定 ...
- Ubuntu RDP服务
这里先简单了解一下rdp和vnc的区别 VNC 就像我们使用向日葵一下远程操作别的电脑一下,只能有一人在操作 RDP 是无感式操作,在别人没知觉的情况下控制新的桌面 这是我个人的理解,有不对的地方望各 ...
- vue+vant实现省市联动(van-area)组件(包含比较全面的全国省市数组数据)
组件库太香了,人家nb,自己写的都是** 效果: 1.安装vant库以及main.js的配置 2.一般结合van-popup组件 </template> <van-popup v-m ...
- 欧几里得算法求最大公因数gcd原理证明
要证明欧几里得算法原理,首先需要证明下面两个定理(其中a,b都是整数): 1 如果c可以整除a,同时c也可以整除b,那么c就可以整除au + bv(u,v是任意的整数). 这个定理的证明很简单,$\f ...
- es请求方式调用
Es基础 关系: ElasticSearch-> mysql index (索引)-> 数据库 Documents(文档) -> row(行) Fileds(字段)-> col ...
- Nifi:Nifi中的Controller Service
Service简介 首先Nifi中的Controller Service 和我们MVC概念中的Controller Service不是一个概念,Nifi中的Controller Service更像是和 ...
- C# 如何获取本机IP
百度搜索的方案 如果你去百度C#如何获取本机IP,那么大概率的你会得到以下的几段代码,第一种就是这样: string name = Dns.GetHostName(); IPAddress[] ipa ...
- 使用rem、动态vh自适应移动端
前言 这是我的 模仿抖音 系列文章的第六篇 第一篇:200行代码实现类似Swiper.js的轮播组件 第二篇:实现抖音 "视频无限滑动"效果 第三篇:Vue 路由使用介绍以及添加转 ...
- uniapp 添加操作
1 分析: 2 需要动态的向数据库中插入数据,既然要进行添加操作,就会触发一个点击的事件,所以我们的第一步就是要先绑定事件 3 向服务器进行请求接口操作,请求的方法为 'POST' 4 最后就是在me ...
- LeetCode 690. Employee Importance 员工的重要性(C++/Java)
题目: You are given a data structure of employee information, which includes the employee's unique id, ...