https://atcoder.jp/contests/abc244/tasks
G - Construct Good Path n点m边 简单无向连通图.
边长=点数
构造一条 长度小于4N的边, 让点i出现次数%2 == s[i]
范围 n 1e5
m 1e5
2s
1024mb
我的思路 说是图, 但是树也是图, 所以树应该也可以, 所以 先从图中随便提出个树再在树上做
显然树上有个不满足个数的方案
根到点再返回根, 这样走完所有再决定 根需要奇数次还是偶数次
但这样搞次数肯定超了
考虑树上两种,一个是成链,一个是分叉
链上 a-b-c-d-e-f-g-h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 abcdefgh xxxxxxxx x x x x x x x x x x x x x
然后是分叉, 考虑分叉= 多个链合并起来, 主要关注一下合并处的奇偶
可以看成a b c d e
和 c f g
两条链
c的奇偶性 = 原来c的奇偶性 + 1(额外链数)
似乎就没了, 因为新增的链 对 链头的重复可以看成链尾上多一次,这样每个点次数不超过4, 最后判断根要奇还是偶即可
但是感觉好难写啊
题解 反复横跳!!!!
dfs(u):
u -> dfs(子节点1) -> u -> (如果字节点1不满足奇偶增加 (子节点1->u) ) -> dfs(子节点2) -> u -> (如果字节点1不满足奇偶增加 (子节点2->u) ) -> dfs(子节点3) -> u -> (如果字节点1不满足奇偶增加 (子节点3->u) ) ->
代码 https://atcoder.jp/contests/abc244/submissions/35031281
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<(int)n;i++) int read () {int r;scanf ("%d" ,&r);return r;}std::vector<int > G[100010 ]; char s[100010 ];bool vis[100010 ];std::vector<int > ans; void add (int u) { ans.push_back (u); s[u]^=1 ; } void dfs (int v) { vis[v] = true ; add (v); for (auto u : G[v]) if (!vis[u]) { dfs (u); add (v); if (s[u] == 1 ){ add (u); add (v); } } } int main (void ) { int n = read (); int m = read (); rep (i,1 ,m+1 ){ int u = read (); int v = read (); G[u].push_back (v); G[v].push_back (u); } scanf ("%s" ,s+1 ); rep (i,1 ,n+1 ) s[i] -= '0' ; dfs (1 ); printf ("%d\n" ,(int )ans.size () - s[1 ]); rep (i,s[1 ],ans.size ()) printf ("%d " ,ans[i]); printf ("\n" ); return 0 ; }
Ex - Linear Maximization 二维平面点集S, 初始空
q次操作
每次集合中增加点 (xi,yi), 然后查询S中最大的 (aix+biy)
范围 Q 2e5
xi,yi,ai,bi [-1e9,1e9]
5s
1024mb
我的思路 什么概念呢, ax+by 最大, 若 b>0, 就是 (a/b)x+y 最大
就是斜率(a/b) 的切线去切这个点, 所以需要维护的是一个所有点的凸包, 再根据斜率需求,决定在哪一段二分乱搞一下? set + lower_bound ?
1 2 3 4 5 6 7 -- / \ / \ | \ | / \ / \__/
题解 这里用 inner product 来解释的, 就是 (A,B) 方向上最远的
如果所有点都不是动态加的,而是固定以后,来求q询问
可见最大值 一定在凸包顶点上, 可以考虑三分
如果点是动态加的
那么就是需要动态维护S的凸包
需要用平衡二叉搜索树, 比如 __gnu_pbds::tree 可以快速查询第n个是什么
但有不少语言并不像C++ 有这样的库
可以看成 区间查询, 也就是没有点插入, 而是每次查询一个点下标区间中的凸包的一个切线
而查询的区间, 可以划分成log级别个维护好的区间去做查询
代码 https://atcoder.jp/contests/abc244/submissions/35053588
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <bits/stdc++.h> typedef long long ll;#define rep(i,a,n) for (int i=a;i<(int)n;i++) ll read () {ll r;scanf ("%lld" ,&r);return r;}using Point = std::pair<ll, ll>;const ll INF = 0x3f3f3f3f3f3f3f3f ;Point sub (Point a,Point b) { return {a.first-b.first,a.second-b.second}; } ll cross (Point a, Point b, Point c) { auto [x0,y0] = sub (a,b); auto [x1,y1] = sub (c,b); return x1*y0 -x0*y1; } struct ConvexHull { std::vector<Point> lower; std::vector<Point> upper; ConvexHull (Point p): lower{p}, upper{p}{} ConvexHull (const ConvexHull& a, const ConvexHull& b){ { std::vector<Point> v; std::merge (a.lower.begin (), a.lower.end (), b.lower.begin (), b.lower.end (), back_inserter (v)); for (Point p : v){ while (lower.size () >= 2 && cross (lower.rbegin ()[1 ], lower.back (), p) <= 0 ) lower.pop_back (); lower.push_back (p); } } { std::vector<Point> v; std::merge (a.upper.begin (), a.upper.end (), b.upper.begin (), b.upper.end (), back_inserter (v)); for (Point p : v){ while (upper.size () >= 2 && cross (upper.rbegin ()[1 ], upper.back (), p) >= 0 ) upper.pop_back (); upper.push_back (p); } } } ll get (ll A, ll B) const { auto & s = B < 0 ? lower : upper; auto f = [&](ll i){ return A * s[i].first + B * s[i].second; }; ll l = 0 ; ll r = s.size () - 1 ; while (r-l >= 3 ){ ll x1 = (2 *l+r)/3 ; ll x2 = (l+2 *r)/3 ; ll f1 = f (x1); ll f2 = f (x2); if (f1 < f2) l = x1; else r = x2; } ll ans = -INF; rep (i,l,r+1 ) ans = std::max (ans, f (i)); return ans; } }; int main () { ll Q = read (); std::vector<ConvexHull> S; rep (i,1 ,Q+1 ){ ll X = read (); ll Y = read (); ll A = read (); ll B = read (); S.emplace_back (std::pair{X, Y}); for (int j = i; j%2 ==0 ; j/=2 ){ S.rbegin ()[1 ] = ConvexHull (S.rbegin ()[1 ], S.back ()); S.pop_back (); } ll ans = -INF; for (auto & s : S) ans = std::max (ans, s.get (A, B)); printf ("%lld\n" ,ans); } }
总结 G
不会编码.. 不会构造
Ex
计算几何没几个会的, 感觉核心问题还是计算集合没写熟悉吧
哦 原来维护这样的凸包不用去分什么 分割点或者上下部分
而是直接一个vector维护 一圈凹,一个vector维护一圈凸
然后两个合并的时候, 就是 先拼起来, 然后凹与凹合并, 凸与凸合并, 也没有去维护头部与后续的关系, 就是虽然每个都不严格, 但是它们的最优 = 所求的最优,
然后因为本身凸包就可能达到n个点, 所以依靠的是树状数组的思想而不是凸包本身,来让复杂度 是 n log n 级别,
一个更复杂的题 洛谷 P3309 SDOI2014
动态插入点,
强制在线 动态询问 区间下标[l..r]中的点,中给定斜率下最大值
参考 官方题解
pbds(平板电视)
1 2 3 4 #include <bits/extc++.h> using namespace __gnu_pbds;
https://en.cppreference.com/w/cpp/iterator/back_inserter