牛客周赛 Round 57(A - D, F - G)

传送门

A - 小红喜欢1

原题

小红拿到了一个长度为 5 的、仅由 0 和 1 组成的数组 ,其中恰好有一个 1 ,其余元素都是 0 。请你直接输出 1 所在的位置。

输入

在一行上输入五个整数 a 1 , a 2 , a 3 , a 4 , a 5 ( 0 ≤ a i ≤ 1 ) a_{1}, a_{2},a_{3},a_{4},a_{5}(0 \le a_{i} \le 1) a1,a2,a3,a4,a5(0ai1),代表小红拿到的数组

输出

在一行上输出一个整数,代表 1 1 1 所在的位置

测试样例

input1
0 0 1 0 0
output1
3

分析

  • 这一题属于签到题,按顺序输入五个数,打印1所在的位置即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

int main() {
	
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	int ans = 0;
	for(int i = 1; i <= 5; i++) {
		int m;
		std::cin >> m;
		if(m == 1) {
			ans = i;
		}
	}
	std::cout << ans;

	return 0;
}

B - 小红的树切割

原题

小红定义一棵树是“好树”,当且仅当任意相邻的两个节点的颜色不同(特殊的,一个节点组成的树一定是好树)。
现在小红拿到了一棵树,一些节点被染成红色,其余节点为白色。小红希望切割若干条边形成森林,使得森林的每棵树都是“好树”。小红想知道,最少需要切割多少条边?

输入

第一行输入一个整数 n ( 2 ≤ n ≤ 1 0 5 ) n(2 \le n \le 10^5) n(2n105)代表树的节点数量。
第二行输入一个长度为 n n n,且仅由 R R R W W W组成的字符串 s s s,第 i i i个字符代表第 i i i 个节点的颜色,其中 R R R 代表红色, W W W代表白色.
此后 n − 1 n - 1 n1行,第 i i i 行输入两个整数 u i u_{i} ui v i ( 1 ≤ u i , v i ≤ n ; u i ≠ v i ) v_{i}(1 \le u_{i}, v_{i} \le n; u_{i} \not= v_{i}) vi(1ui,vin;ui=vi),链接:表示树上第 i i i 条边连接节点 u i u_{i} ui v i v_{i} vi。保证树联通,没有重边。

输出

在一行上输出一个整数,代表最少需要切割的边数。

测试样例

input1
4
RWWR
1 2
2 3
3 4
output1
1

分析

  • 这题类似二分图匹配,只不过我们这里是断边,所以只需要判断相邻的两个点是否是同一个颜色,如果是,则删边,否则 c o n t i n u e continue continue
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

int main() {
	
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	int n;
	std::cin >> n;
	std::string s;
	std::cin >> s;
	std::vector<std::vector<int>> adj(n);
	for(int i = 0; i < n - 1; i++) {
		int u, v;
		std::cin >> u >> v;
		u--;
		v--;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

	int ans = 0;

	auto dfs = [&](auto self, int u, int fa) -> void {

		for(auto v : adj[u]) {
			if(v == fa) {
				continue;
			}
			if(s[u] == s[v]) {
				ans++;
			}
			self(self, v, u);
		}
	};

	dfs(dfs, 0, -1);
	std::cout << ans;

	return 0;
}

C - 小红的双好数

原题

小红定义 n n n k − k - k好数,当且仅当 n n n k k k 进制表示下,每一位都不大于 1 1 1 。例如, 30 30 30 3 − 3- 3好数,因为 30 30 30 在三进制表示下是 ( 1010 ) 3 (1010)_{3} (1010)3.
现在输入了一个正整数 n n n,小红希望你找到两个不同的正整数 k 1 k_{1} k1 k 2 ( 2 ≤ k 1 < k 2 ≤ 1 0 18 ) k_{2}(2 \le k1 < k2 \le 10^{18}) k2(2k1<k21018),满足 n n n 既是 k 1 − k_{1}- k1好数,也是 k 2 − k_{2}- k2好数,你能帮帮她吗?

输入

在一行上输入一个整数 n ( 1 ≤ n ≤ 1 0 18 ) n(1 \le n \le 10^{18}) n(1n1018)代表待求解的数字。

输出

如果在 [ 2 , 1 0 18 ] [2, 10^{18}] [2,1018]范围内无解,直接在一行上输出 N O NO NO;否则,先在第一行上输出 Y E S YES YES;随后,在第二行上输出两个正整数 k 1 , k 2 ( 2 ≤ k 1 < k 2 ≤ 1 0 18 ) k_{1}, k_{2}(2 \le k_{1} < k_{2} \le 10^{18}) k1,k2(2k1<k21018),代表一组合法解。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。

测试样例

input1
5
output1
YES
2 4

input2
2
output2
NO

分析

  • 首先对于任意数,肯定都有一个2进制符合题意,那么我们现在要寻找另一个,假设是 x x x 进制,设 n = ( 11 ) x n = (11)_{x} n=(11)x,可解得 x = n − 1 x = n - 1 x=n1,因此 n − 1 n - 1 n1即是另一个进制,此时只需要对于 1   2   3 1 \space 2 \space 3 1 2 3 特判一下即可.
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

int main() {
	
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	i64 n;
	std::cin >> n;
	if(n == 2) {
		std::cout << "NO\n";
        return 0;
	}
	std::cout << "YES\n";
	if(n == 1) {
		std::cout << 2 << " " << 3;
	} else if(n != 3){
		std::cout << 2 << " " << n - 1;
	} else {
		std::cout << 2 << " " << 3;
	}
	return 0;
}

D - 小红的线段

原题

在平面直角坐标系中有 2 × n 2 \times n 2×n个点和一条直线 y = k x + b y = kx + b y=kx+b.
小红准备在这 2 × n 2 \times n 2×n个点中连 n n n 条线段(每个点只能连接一次),她希望 和直线有交点的线段 的数量尽可能多。
请你给出一个连接方案。

输入

第一行输入三个整数 n , k , b ( 1 ≤ n ≤ 1 0 3 ; − 1 0 5 ≤ k , b ≤ 1 0 5 ) n,k,b(1 \le n \le 10^3; -10^5 \le k,b \le 10^5) n,k,b(1n103;105k,b105)表示有 2 × n 2 \times n 2×n个点和直线 y = k x + b y = kx + b y=kx+b.
接下来 2 × n 2 \times n 2×n行,每行两个整数 x , y ( − 1 0 5 ≤ x , y ≤ 1 0 5 ) x,y(-10^5 \le x,y \le 10^5) x,y(105x,y105)表示一个点的坐标。保证没有两个点的坐标相同。

输出

先输出一个整数 m m m,表示有 m m m 条线段和直线有交点。
接下来 n n n 行,第 i i i 行输出两个正整数 u i , v i ( 1 ≤ u i , v i ≤ 2 × n ; u i ≠ v i ) u_{i}, v_{i}(1 \le u_{i}, v_{i} \le 2 \times n; u_{i} \not = v_{i}) ui,vi(1ui,vi2×n;ui=vi)和一个字符 Y Y Y N N N链接:代表连接第 u i u_{i} ui 个点和第 v i v_{i} vi 个点,以及是否和直线有交点。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。

测试样例

input1
2 -1 2
0 0
0 1
1 0
1 1
output1
1
1 2 N
3 4 Y

分析

  • 要使线段与直线相交,那么线段的两个点应该在直线的两边或者其中一个在直线上,因此,判断每个点与直线的关系,然后再进行处理即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

// 点
template<class T>
struct Point {
    T x;
    T y;
    Point(const T &x_ = 0, const T &y_ = 0) : x(x_), y(y_) {}
    
    template<class U>
    operator Point<U>() {
        return Point<U>(U(x), U(y));
    }
    Point &operator+=(const Point &p) & {
        x += p.x;
        y += p.y;
        return *this;
    }
    Point &operator-=(const Point &p) & {
        x -= p.x;
        y -= p.y;
        return *this;
    }
    Point &operator*=(const T &v) & {
        x *= v;
        y *= v;
        return *this;
    }
    Point &operator/=(const T &v) & {
        x /= v;
        y /= v;
        return *this;
    }
    Point operator-() const {
        return Point(-x, -y);
    }
    friend Point operator+(Point a, const Point &b) {
        return a += b;
    }
    friend Point operator-(Point a, const Point &b) {
        return a -= b;
    }
    friend Point operator*(Point a, const T &b) {
        return a *= b;
    }
    friend Point operator/(Point a, const T &b) {
        return a /= b;
    }
    friend Point operator*(const T &a, Point b) {
        return b *= a;
    }
    friend bool operator==(const Point &a, const Point &b) {
        return a.x == b.x && a.y == b.y;
    }
    friend std::istream &operator>>(std::istream &is, Point &p) {
        return is >> p.x >> p.y;
    }
    friend std::ostream &operator<<(std::ostream &os, const Point &p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }
};

// 线
template<class T>
struct Line {
    Point<T> a;
    Point<T> b;
    Line(const Point<T> &a_ = Point<T>(), const Point<T> &b_ = Point<T>()) : a(a_), b(b_) {}
};

// 点积
template<class T>
T dot(const Point<T> &a, const Point<T> &b) {
    return a.x * b.x + a.y * b.y;
}

// 叉积
template<class T>
T cross(const Point<T> &a, const Point<T> &b) {
    return a.x * b.y - a.y * b.x;
}

// return x^2 + y^2
template<class T>
T square(const Point<T> &p) {
    return dot(p, p);
}

// return Euclidean distance from (0,0) to the point (x,y)
// ((x - 0)^2 + (y - 0)^2) 取根号
template<class T>
double length(const Point<T> &p) {
    return std::sqrt(square(p));
}

// L线段的长度
template<class T>
double length(const Line<T> &l) {
    return length(l.a - l.b);
}


template<class T>
Point<T> normalize(const Point<T> &p) {
    return p / length(p);
}

// 判断直线l1和l2是否平行或重合
// 平行或重合返回true 否则返回false
template<class T>
bool parallel(const Line<T> &l1, const Line<T> &l2) {
    return cross(l1.b - l1.a, l2.b - l2.a) == 0;
}

// a, b两点的距离
template<class T>
double distance(const Point<T> &a, const Point<T> &b) {
    return length(a - b);
}

// p点到直线l的距离
template<class T>
double distancePL(const Point<T> &p, const Line<T> &l) {
    return std::abs(cross(l.a - l.b, l.a - p)) / length(l);
}

// p点到线段l的距离
template<class T>
double distancePS(const Point<T> &p, const Line<T> &l) {
    if (dot(p - l.a, l.b - l.a) < 0) {
        return distance(p, l.a);
    }
    if (dot(p - l.b, l.a - l.b) < 0) {
        return distance(p, l.b);
    }
    return distancePL(p, l);
}

// 点a逆时针旋转90度
template<class T>
Point<T> rotate(const Point<T> &a) {
    return Point(-a.y, a.x);
}

// 判断是否在x轴的上侧或y轴的右侧
// 是返回1 否返回-1
template<class T>
int sgn(const Point<T> &a) {
    return a.y > 0 || (a.y == 0 && a.x > 0) ? 1 : -1;
}

// 判断点p是否在线段l的上侧
// 是返回true 否返回false
template<class T>
bool pointOnLineLeft(const Point<T> &p, const Line<T> &l) {
    return cross(l.b - l.a, p - l.a) > 0;
}

// 线段l1和l2的交点坐标
template<class T>
Point<T> lineIntersection(const Line<T> &l1, const Line<T> &l2) {
    return l1.a + (l1.b - l1.a) * (cross(l2.b - l2.a, l1.a - l2.a) / cross(l2.b - l2.a, l1.a - l1.b));
}

// 判断点p是否在线段l上
// 是返回true 否返回false
template<class T>
bool pointOnSegment(const Point<T> &p, const Line<T> &l) {
    return cross(p - l.a, l.b - l.a) == 0 && std::min(l.a.x, l.b.x) <= p.x && p.x <= std::max(l.a.x, l.b.x)
        && std::min(l.a.y, l.b.y) <= p.y && p.y <= std::max(l.a.y, l.b.y);
}

// 点a是否在由p中的点组成的面中
// 是返回true 否返回false
template<class T>
bool pointInPolygon(const Point<T> &a, const std::vector<Point<T>> &p) {
    int n = p.size();
    for (int i = 0; i < n; i++) {
        if (pointOnSegment(a, Line(p[i], p[(i + 1) % n]))) {
            return true;
        }
    }
    
    int t = 0;
    for (int i = 0; i < n; i++) {
        auto u = p[i];
        auto v = p[(i + 1) % n];
        if (u.x < a.x && v.x >= a.x && pointOnLineLeft(a, Line(v, u))) {
            t ^= 1;
        }
        if (u.x >= a.x && v.x < a.x && pointOnLineLeft(a, Line(u, v))) {
            t ^= 1;
        }
    }
    
    return t == 1;
}

// 0 : not intersect(不相交)
// 1 : strictly intersect(严格相交)
// 2 : overlap(重叠)
// 3 : intersect at endpoint(在端点处相交)
// 线段相交
template<class T>
std::tuple<int, Point<T>, Point<T>> segmentIntersection(const Line<T> &l1, const Line<T> &l2) {
    if (std::max(l1.a.x, l1.b.x) < std::min(l2.a.x, l2.b.x)) {
        return {0, Point<T>(), Point<T>()};
    }
    if (std::min(l1.a.x, l1.b.x) > std::max(l2.a.x, l2.b.x)) {
        return {0, Point<T>(), Point<T>()};
    }
    if (std::max(l1.a.y, l1.b.y) < std::min(l2.a.y, l2.b.y)) {
        return {0, Point<T>(), Point<T>()};
    }
    if (std::min(l1.a.y, l1.b.y) > std::max(l2.a.y, l2.b.y)) {
        return {0, Point<T>(), Point<T>()};
    }
    if (cross(l1.b - l1.a, l2.b - l2.a) == 0) {
        if (cross(l1.b - l1.a, l2.a - l1.a) != 0) {
            return {0, Point<T>(), Point<T>()};
        } else {
            auto maxx1 = std::max(l1.a.x, l1.b.x);
            auto minx1 = std::min(l1.a.x, l1.b.x);
            auto maxy1 = std::max(l1.a.y, l1.b.y);
            auto miny1 = std::min(l1.a.y, l1.b.y);
            auto maxx2 = std::max(l2.a.x, l2.b.x);
            auto minx2 = std::min(l2.a.x, l2.b.x);
            auto maxy2 = std::max(l2.a.y, l2.b.y);
            auto miny2 = std::min(l2.a.y, l2.b.y);
            Point<T> p1(std::max(minx1, minx2), std::max(miny1, miny2));
            Point<T> p2(std::min(maxx1, maxx2), std::min(maxy1, maxy2));
            if (!pointOnSegment(p1, l1)) {
                std::swap(p1.y, p2.y);
            }
            if (p1 == p2) {
                return {3, p1, p2};
            } else {
                return {2, p1, p2};
            }
        }
    }
    auto cp1 = cross(l2.a - l1.a, l2.b - l1.a);
    auto cp2 = cross(l2.a - l1.b, l2.b - l1.b);
    auto cp3 = cross(l1.a - l2.a, l1.b - l2.a);
    auto cp4 = cross(l1.a - l2.b, l1.b - l2.b);
    
    if ((cp1 > 0 && cp2 > 0) || (cp1 < 0 && cp2 < 0) || (cp3 > 0 && cp4 > 0) || (cp3 < 0 && cp4 < 0)) {
        return {0, Point<T>(), Point<T>()};
    }
    
    Point p = lineIntersection(l1, l2);
    if (cp1 != 0 && cp2 != 0 && cp3 != 0 && cp4 != 0) {
        return {1, p, p};
    } else {
        return {3, p, p};
    }
}

// 线段l1和l2的距离
template<class T>
double distanceSS(const Line<T> &l1, const Line<T> &l2) {
    if (std::get<0>(segmentIntersection(l1, l2)) != 0) {
        return 0.0;
    }
    return std::min({distancePS(l1.a, l2), distancePS(l1.b, l2), distancePS(l2.a, l1), distancePS(l2.b, l1)});
}

// 线段l是否在由p中的点组成的面中
template<class T>
bool segmentInPolygon(const Line<T> &l, const std::vector<Point<T>> &p) {
    int n = p.size();
    if (!pointInPolygon(l.a, p)) {
        return false;
    }
    if (!pointInPolygon(l.b, p)) {
        return false;
    }
    for (int i = 0; i < n; i++) {
        auto u = p[i];
        auto v = p[(i + 1) % n];
        auto w = p[(i + 2) % n];
        auto [t, p1, p2] = segmentIntersection(l, Line(u, v));
        
        if (t == 1) {
            return false;
        }
        if (t == 0) {
            continue;
        }
        if (t == 2) {
            if (pointOnSegment(v, l) && v != l.a && v != l.b) {
                if (cross(v - u, w - v) > 0) {
                    return false;
                }
            }
        } else {
            if (p1 != u && p1 != v) {
                if (pointOnLineLeft(l.a, Line(v, u))
                    || pointOnLineLeft(l.b, Line(v, u))) {
                    return false;
                }
            } else if (p1 == v) {
                if (l.a == v) {
                    if (pointOnLineLeft(u, l)) {
                        if (pointOnLineLeft(w, l)
                            && pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    } else {
                        if (pointOnLineLeft(w, l)
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    }
                } else if (l.b == v) {
                    if (pointOnLineLeft(u, Line(l.b, l.a))) {
                        if (pointOnLineLeft(w, Line(l.b, l.a))
                            && pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    } else {
                        if (pointOnLineLeft(w, Line(l.b, l.a))
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    }
                } else {
                    if (pointOnLineLeft(u, l)) {
                        if (pointOnLineLeft(w, Line(l.b, l.a))
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    } else {
                        if (pointOnLineLeft(w, l)
                            || pointOnLineLeft(w, Line(u, v))) {
                            return false;
                        }
                    }
                }
            }
        }
    }
    return true;
}


template<class T>
std::vector<Point<T>> hp(std::vector<Line<T>> lines) {
    std::sort(lines.begin(), lines.end(), [&](auto l1, auto l2) {
        auto d1 = l1.b - l1.a;
        auto d2 = l2.b - l2.a;
        
        if (sgn(d1) != sgn(d2)) {
            return sgn(d1) == 1;
        }
        
        return cross(d1, d2) > 0;
    });
    
    std::deque<Line<T>> ls;
    std::deque<Point<T>> ps;
    for (auto l : lines) {
        if (ls.empty()) {
            ls.push_back(l);
            continue;
        }
        
        while (!ps.empty() && !pointOnLineLeft(ps.back(), l)) {
            ps.pop_back();
            ls.pop_back();
        }
        
        while (!ps.empty() && !pointOnLineLeft(ps[0], l)) {
            ps.pop_front();
            ls.pop_front();
        }
        
        if (cross(l.b - l.a, ls.back().b - ls.back().a) == 0) {
            if (dot(l.b - l.a, ls.back().b - ls.back().a) > 0) {
                
                if (!pointOnLineLeft(ls.back().a, l)) {
                    assert(ls.size() == 1);
                    ls[0] = l;
                }
                continue;
            }
            return {};
        }
        
        ps.push_back(lineIntersection(ls.back(), l));
        ls.push_back(l);
    }
    
    while (!ps.empty() && !pointOnLineLeft(ps.back(), ls[0])) {
        ps.pop_back();
        ls.pop_back();
    }
    if (ls.size() <= 2) {
        return {};
    }
    ps.push_back(lineIntersection(ls[0], ls.back()));
    
    return std::vector(ps.begin(), ps.end());
}

int main() {
	
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	i64 n, k, b;
	std::cin >> n >> k >> b;
	Line<i64> ly;
	ly.a.x = -10000000;
	ly.a.y = -10000000 * k + b;
	ly.b.x = 10000000;
	ly.b.y = 10000000 * k + b;
	std::set<int> st1, st2, st3;
	for(int i = 0; i < 2 * n; i++) {
		Point<i64> p;
		std::cin >> p.x >> p.y;
		if(p.y == k * p.x + b) {
			st3.insert(i);
			continue;
		}
		if(pointOnLineLeft(p, ly)) {
			st1.insert(i);
		} else {
			st2.insert(i);
		}
	}
	int n1 = st1.size(), n2 = st2.size(), n3 = st3.size();
	int gap = std::abs(n1 - n2);
	// std::cout << st1.size() << " " << st2.size() << " " << st3.size() << "\n";
	std::cout << std::min(n1, n2) + std::min(gap, n3) + std::max(0, n3 - gap) / 2 << "\n";
	while(st3.size()) {
		if(st1.size() == 0 && st2.size() == 0) {
			break;
		}
		if(st1.size() > st2.size()) {
			std::cout << *st1.begin() + 1 << " " << *st3.begin() + 1 << " Y\n";
			st1.erase(st1.begin());
			st3.erase(st3.begin());
		} else {
			std::cout << *st2.begin() + 1 << " " << *st3.begin() + 1 << " Y\n";
			st2.erase(st2.begin());
			st3.erase(st3.begin());
		}
	}


	while(st1.size() && st2.size()) {
		std::cout << *st1.begin() + 1 << " " << *st2.begin() + 1 << " Y\n";
		st1.erase(st1.begin());
		st2.erase(st2.begin());
	}

	while(st1.size()) {
		std::cout << *st1.begin() + 1 << " ";
		st1.erase(st1.begin());
		std::cout << *st1.begin() + 1 << " N\n";
		st1.erase(st1.begin());
	}

	while(st2.size()) {
		std::cout << *st2.begin() + 1 << " ";
		st2.erase(st2.begin());
		std::cout << *st2.begin() + 1 << " N\n";
		st2.erase(st2.begin());
	}

	while(st3.size()) {
		std::cout << *st3.begin() + 1 << " ";
		st3.erase(st3.begin());
		std::cout << *st3.begin() + 1 << " Y\n";
		st3.erase(st3.begin());
	}


	return 0;
}

F - 小红的数组操作

原题

小红拿到了 n n n 个数组,她有以下两种操作:
1.输入 1   i   j   x 1\space i \space j \space x 1 i j x将第 i i i 个数组的第 j j j 个元素修改为 x x x
2.输入 2   i 2 \space i 2 i查询前 i i i 个数组的最小值。

输入

第一行输入一个正整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \le n \le 10^5) n(1n105),代表数组的个数。
接下来 n n n 行,每行先输入一个正整数 m i ( 1 ≤ m i ≤ 1 0 5 ) m_{i}(1 \le m_{i} \le 10^5) mi(1mi105),代表数组的长度,接下来输入 m i m_{i} mi个整数, a i j ( 1 ≤ a i j ≤ 1 0 9 ) a_{ij}(1 \le a_{ij} \le 10^9) aij(1aij109)表示数组的元素。
接下来输入一个正整数 q ( 1 ≤ q ≤ 1 0 5 ) q(1 \le q \le 10^5) q(1q105),代表操作的次数。
接下来 q q q 行,每行先输入一个正整数 t ( 1 ≤ t ≤ 2 ) t(1 \le t \le 2) t(1t2)表示操作的类型。
如果 t = 1 t = 1 t=1。则接下来输入三个正整数 i , j , x ( 1 ≤ i ≤ n ; 1 ≤ j ≤ m ; 1 ≤ x ≤ 1 0 9 ) i, j, x(1 \le i \le n;1 \le j \le m;1 \le x \le 10^9) i,j,x(1in;1jm;1x109),表示将第 i i i个数组的第 j j j个元素修改为 x x x
如果 t = 2 t = 2 t=2,则接下来输入一个正整数 i ( 1 ≤ i ≤ n ) i(1 \le i \le n) i(1in),表示查询前 i i i 个数组的最小值
保证至少有一个查询操作,且 ∑ i = 1 n m i ≤ 1 0 5 \sum_{i = 1}^n m_{i} \le 10^5 i=1nmi105

输出

对于每个查询操作,输出一个整数,表示查询的结果。

测试样例

input1
4
3 1 2 3
3 4 5 6
4 7 8 9 10
2 1 2
5
2 2
1 1 1 10
2 3
1 1 2 11
2 2
output1
1
2
3

分析

  • 这题其实是线段树的板子题,只不过修改的操作需要进行处理,具体处理方式见代码
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

template<class Info>
struct SegmentTree {
    int n;
    std::vector<Info> info;
    SegmentTree() : n(0) {}
    SegmentTree(int n_, Info v_ = Info()) {
        init(n_, v_);
    }
    template<class T>
    SegmentTree(std::vector<T> init_) {
        init(init_);
    }
    void init(int n_, Info v_ = Info()) {
        init(std::vector(n_, v_));
    }
    template<class T>
    void init(std::vector<T> init_) {
        n = init_.size();
        info.assign(4 << std::__lg(n), Info());
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                info[p] = init_[l];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m, r);
            pull(p);
        };
        build(1, 0, n);
    }
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }
    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return Info();
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
    }
    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n, l, r);
    }
    template<class F>
    int findFirst(int p, int l, int r, int x, int y, F pred) {
        if (l >= y || r <= x || !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = (l + r) / 2;
        int res = findFirst(2 * p, l, m, x, y, pred);
        if (res == -1) {
            res = findFirst(2 * p + 1, m, r, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findFirst(int l, int r, F pred) {
        return findFirst(1, 0, n, l, r, pred);
    }
    template<class F>
    int findLast(int p, int l, int r, int x, int y, F pred) {
        if (l >= y || r <= x || !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = (l + r) / 2;
        int res = findLast(2 * p + 1, m, r, x, y, pred);
        if (res == -1) {
            res = findLast(2 * p, l, m, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findLast(int l, int r, F pred) {
        return findLast(1, 0, n, l, r, pred);
    }
};


const int inf = 1E9 + 7;

struct Info {
	int ans = inf;

};

Info operator+(Info a, Info b) {

	Info c{};
	c.ans = std::min(a.ans, b.ans);
	return c;
}

int main() {
	
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	int n;
	std::cin >> n;
	std::multiset<int> min[n];
	std::vector<std::vector<int>> s(n);
	for(int i = 0; i < n; i++) {
		int m;
		std::cin >> m;
		for(int j = 0; j < m; j++) {
			int k;
			std::cin >> k;
			s[i].push_back(k);
			min[i].insert(k);
		}
	}

	SegmentTree<Info> seg(n);
	for(int i = 0; i < n; i++) {
		seg.modify(i, {*min[i].begin()});
	}
	int q;
	std::cin >> q;

	for(int i = 0; i < q; i++) {
		int t;
		std::cin >> t;
		if(t == 1) {
			int a, b, x;
			std::cin >> a >> b >> x;
			a--;
			b--;
			min[a].erase(s[a][b]);
			s[a][b] = x;
			min[a].insert(x);
			seg.modify(a, {*min[a].begin()});
		} else {
			int k;
			std::cin >> k;
			std::cout << seg.rangeQuery(0, k).ans << "\n";
		}
	}


	return 0;
}

G - 小红的双排列构造

原题

小红希望你构造一个长度为 2 × n 2 \times n 2×n 的双排列,满足:恰好有 k k k 个长度为 n n n 的区间为排列。你能帮帮她吗?
长度为 n 的排列是由 1 到 n 中的 n 个不同整数、按任意顺序组成的数组,其中每个整数恰好出现一次。例如,[2,3,1,5,4] 是一个排列,但 [1,2,2] 不是一个排列(数组中的 2 出现了两次),[1,3,4] 也不是一个排列(长度为 3 但数组中有 4)。
定义双排列为两个长度为 𝑛 的排列打乱顺序后得到的数组,或者说是由 1 到 𝑛 中的 𝑛 个不同整数、按任意顺序组成的数组,其中每个整数恰好出现两次。

输入

在一行上输入两个整数 n , k ( 1 ≤ n ≤ 1 0 5 ; 0 ≤ k ≤ n + 1 ) n,k(1 \le n \le 10^5; 0 \le k \le n + 1) n,k(1n105;0kn+1) 代表要求构造的双排列长度及限制。

输出

如果无法构造出符合要求的双排列,直接在一行上输出 −1 ;否则,在一行上输出 2 × n 2 \times n 2×n 个整数,代表构造的双排列。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。

测试样例

input1
3 4
output1
1 2 3 1 2 3

input2
4 1
outpu2
3 4 3 1 2 2 4 1

分析

  • 分情况讨论

1.当 k = 0,那么直接输出 1 1 2 2 3 3 …即可,需要注意 n ≤ 2 \le 2 2时是不存在 k = 0 k = 0 k=0的情况
2. 当 k = 1,那么可以保留一个排列,拆开另一个排列,这样可以保证只有一个,特判n = 1 的情况
3. 当k > 1,先保留一个排列 1 2 3 4 …然后再在后面插入尾插排列的前 k - 1个数,再打乱剩下的数即可

#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

int main() {
	
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);

	int n, k;
	std::cin >> n >> k;
	if(k == 0) {
		if(n <= 2) {
			std::cout << -1;
		} else {
			for(int i = 1; i <= n; i++) {
				std::cout << i << " " << i << " ";
			}
		}
	} else if(k == 1) {
		if(n == 1) {
			std::cout << -1;
		} else {
			std::vector<int> ans;
			ans.push_back(1);
			for(int i = 1; i <= n; i++) {
				ans.push_back(i);
			}
			ans.push_back(n);
			for(int i = 2; i < n; i++) {
				ans.push_back(i);
			}
			for(auto x : ans) {
				std::cout << x << " ";
			}
		}
	} else {
		std::vector<int> ans;
		for(int i = 1; i <= n; i++) {
			ans.push_back(i);
		}
		for (int i = 1; i <= k - 2; i++) {
			ans.push_back(i);
		}
		for (int i = n; i > k - 2 && i; i--) {
			ans.push_back(i);
		}
		for(auto x : ans) {
			std::cout << x << " ";
		}
	}
	


	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值