牛客小白月赛105
牛客小白月赛105
lz的吃饭问题
思路
判断 \(a \times b\) 与 \(c \times d\) 的关系即可。
代码
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int a, b, c, d;
std::cin >> a >> b >> c >> d;
std::cout << (a * b < c * d ? "lz" : "gzy") << "\n";
return 0;
}
lz的数字问题
思路
感觉这个题很毒瘤
输入长度有 \(2\times 10^5\),所以输入也得用字符串,然后对于是整数部分的,就给他加上小数点,然后两个都加 \(6\) 个 \(0\),最后取小数点后 \(6\) 位的长度即可(从 \(0\) 开始需要多加一位)。
代码
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string a, b;
std::cin >> a >> b;
if (a.find('.') == -1) {
a += '.';
}
if (b.find('.') == -1) {
b += '.';
}
a += "000000", b += "000000";
auto check = [](std::string s)->std::string{
return s.substr(0,s.find('.') + 7);
};
std::cout << (check(a) == check(b) ? "YES" : "NO") << "\n";
return 0;
}
lz的蛋挞问题
思路
感觉也是个毒瘤题
题意就是让找有多少个割点,但是一堆if-else
判断也可以,不过我 \(WA\) 了三发后就不想写了,直接找了个割点板子,就是对于单个点就是一个联通块的情况下需要特判即可。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 1e6 + 20;
int head[N], edge[N << 1], Next[N << 1], tot;
int dfn[N], low[N], n, m, num, root, ans;
bool cut[N];
void add_edge(int a, int b)
{
edge[++tot] = b;
Next[tot] = head[a];
head[a] = tot;
}
void Tarjan(int x)
{
dfn[x] = low[x] = ++num; //DFS序标记
int flag = 0;
for (int i = head[x]; i; i = Next[i]) //访问所有出边
{
int y = edge[i]; //出边
if (!dfn[y])//不曾访问过,也就是没有标记,可以认为是儿子节点了
{
Tarjan(y);//访问儿子节点y,并且设置边为当前边
low[x] = min(low[x], low[y]); //看看能不能更新,也就是定义中的,subtree(x)中的节点最小值为low[x]
if (low[y] >= dfn[x]) //这就是割点的判定
{
flag++;//割点数量++
if (x != root || flag > 1) //不能是根节点,或者说是根节点,但是有至少两个子树节点是割点
cut[x] = true;
}
}
else low[x] = min(low[x], dfn[y]); //第二类定义,也就是通过1跳不在搜索树上的边,能够抵达subtree(x)的节点
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
cin >> n;
string s[2];
cin >> s[0] >> s[1];
int u[] = {1, -1, 0, 0}, v[] = {0, 0, 1, -1};
for (int i = 0; i < 2; i ++) {
for (int j = 0; j < n; j ++) {
if (s[i][j] == 'x') continue;
bool has = 0;
for (int k = 0; k < 4; k ++) {
int dx = i + u[k], dy = j + v[k];
if (dx >= 0 && dx <= 1 && dy >= 0 && dy < n) {
if (s[dx][dy] == 'x') continue;
add_edge(i * n + j, dx * n + dy);
has = 1;
}
}
if(!has){
ans ++;
}
}
}
for (int i = 0; i < 2 * n; i++){
if (!dfn[i]){
root = i, Tarjan(i);
}
}
for (int i = 0; i < 2 * n; i++) {
if (cut[i]){
ans++;
}
}
cout << ans << "\n";
return 0;
}
lz的染色问题
思路
题意就是说对于所有联通块来说,把联通块里所有点都染成一个颜色所需要的最少花费。
那么我们肯定是把这个联通块里的颜色统计一下,看谁最多,然后就染成哪个色,所以对于单个联通块的贡献就是 \(size-cnt_x\)(\(cnt_x\) 代表颜色最多的数量)。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> c(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> c[i];
}
vector g(n + 1, vector<int>());
while (m--) {
int u, v;
cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
vector<bool> vis(n + 1);
auto check = [&](int x)->int{
map<int, int> mp;
set<int> s;
int res = 0;
queue<int> Q;
Q.push(x);
while (Q.size()) {
auto u = Q.front();
Q.pop();
s.insert(u);
if (vis[u]) continue;
vis[u] = 1;
mp[c[u]] ++;
res = max(res, mp[c[u]]);
for (auto v : g[u]) {
if (!vis[v]) {
Q.push(v);
}
}
}
return s.size() - res;
};
int ans = 0;
for (int i = 1; i <= n; i ++) {
if(!vis[i]){
ans += check(i);
}
}
cout << ans << "\n";
return 0;
}
lz的括号问题
思路
直接求不删除某对括号之前可以删除的匹配数比较难做,但是我们可以反过来求,每对括号被删除前有多少对括号不能被删除,这个就很容易了,用栈记录每个括号匹配的序号时,那么栈里剩下的 (
数量就是不能被删除的,最后计算则答案就是 \(n-cnt_x\)(\(cnt_x\) 表示不能被第 \(x\) 对括号被删除前不能被删除的括号数量),至于不匹配的情况,直接用栈判断即可。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
string s;
cin >> s;
s = " " + s;
vector c(2 * n + 1, 0), has(n + 1, 0);
int now = 0;
stack<int> st;
for (int i = 1; i <= 2 * n; i ++) {
if (s[i] == '(') {
st.push(i);
c[i] = ++now;
} else if (st.size()) {
c[i] = c[st.top()];
has[c[st.top()]] = st.size();
st.pop();
} else {
cout << "-1\n";
return 0;
}
}
for (int i = 1; i <= n; i++) {
cout << n - has[i] << " \n"[i == n];
}
return 0;
}
lz的序列问题
思路
根据题目提供的式子可以写成两个区间的合并,即:
那么上述式子就是区间 \([l,l+1]\) 与区间 \([l+2,r]\) 的贡献合并,这个东西我们可以用线段树去维护,维护一个区间积和区间贡献,那么一个区间的贡献就等于其左儿子的贡献加上左儿子的区间积乘上右儿子的贡献,然后往上 \(PUSH\) 即可。
特别的,区间赋值 \(x\) 以后,那么这段区间的区间积就是等比数列求和即 \(\frac{x(1-x^n)}{1-x}=\frac{x^{n+1}-1}{x-1}\),区间贡献就是 \(r-l+1\),当 \(x=1\) 时需要特判。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
//------取模机------//
using i64 = long long;
template<class T>
constexpr T power(T a, i64 b) {
T res {1};
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
} // 快速幂
constexpr i64 mul(i64 a, i64 b, i64 p) {
i64 res = a * b - i64(1.L * a * b / p) * p;
res %= p;
if (res < 0) {
res += p;
}
return res;
} // 取模乘
template<i64 P>
struct MInt {
i64 x;
constexpr MInt() : x {0} {}
constexpr MInt(i64 x) : x {norm(x % getMod())} {}
static i64 Mod;
constexpr static i64 getMod() {
if (P > 0) {
return P;
} else {
return Mod;
}
}
constexpr static void setMod(i64 Mod_) {
Mod = Mod_;
}//只有P<=0, setMod才生效
constexpr i64 norm(i64 x) const {
if (x < 0) {
x += getMod();
}
if (x >= getMod()) {
x -= getMod();
}
return x;
}
constexpr i64 val() const {
return x;
}
constexpr MInt operator-() const {
MInt res;
res.x = norm(getMod() - x);
return res;
}
constexpr MInt inv() const {
return power(*this, getMod() - 2);
}
constexpr MInt &operator*=(MInt rhs) & {
if (getMod() < (1ULL << 31)) {
x = x * rhs.x % int(getMod());
} else {
x = mul(x, rhs.x, getMod());
}
return *this;
}
constexpr MInt &operator+=(MInt rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MInt &operator-=(MInt rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MInt &operator/=(MInt rhs) & {
return *this *= rhs.inv();
}
friend constexpr MInt operator*(MInt lhs, MInt rhs) {
MInt res = lhs;
res *= rhs;
return res;
}
friend constexpr MInt operator+(MInt lhs, MInt rhs) {
MInt res = lhs;
res += rhs;
return res;
}
friend constexpr MInt operator-(MInt lhs, MInt rhs) {
MInt res = lhs;
res -= rhs;
return res;
}
friend constexpr MInt operator/(MInt lhs, MInt rhs) {
MInt res = lhs;
res /= rhs;
return res;
}
friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
i64 v;
is >> v;
a = MInt(v);
return is;
}
friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
return os << a.val();
}
friend constexpr bool operator==(MInt lhs, MInt rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MInt lhs, MInt rhs) {
return lhs.val() != rhs.val();
}
friend constexpr bool operator<(MInt lhs, MInt rhs) {
return lhs.val() < rhs.val();
}
};
constexpr int MOD[] = {998244353, 1000000007};
using Z = MInt<MOD[1]>;
//------取模机------//
template<class Node>
struct SegmentTree {
#define lc u<<1
#define rc u<<1|1
const int n, N;
vector<Node> tr;
SegmentTree(): n(0) {}
SegmentTree(int n_): n(n_), N(n * 4 + 10) {
tr.reserve(N);
tr.resize(N);
}
SegmentTree(vector<int> init) : SegmentTree(init.size() - 1) {
function<void(int, int, int)> build = [&](int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
init_lazy(tr[u]);
if (l == r) {
tr[u] = {l, r, 0, init[l], init[l]};
return ;
}
i64 mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(tr[u], tr[lc], tr[rc]);
};
build(1, 1, n);
}
void cal_lazy(Node & fa, Node & ch) {
int len = ch.r - ch.l + 1;
Z a = fa.lazy;
ch.mul = power(a, len);
if (a != 1) {
ch.res = (power(a, len + 1) - a) / (a - Z(1));
} else {
ch.res = len;
}
}
void tag_union(Node& fa, Node& ch) {
ch.lazy = fa.lazy;
}
void init_lazy(Node& u) {
u.lazy = 0;
}
void pushdown(int u) {
if (tr[u].lazy.val()) {
cal_lazy(tr[u], tr[lc]);
cal_lazy(tr[u], tr[rc]);
tag_union(tr[u], tr[lc]);
tag_union(tr[u], tr[rc]);
init_lazy(tr[u]);
}
}
void pushup(Node& U, Node& L, Node& R) { //上传
U.l = L.l, U.r = R.r;
U.mul = L.mul * R.mul;
U.res = L.res + R.res * L.mul;
}
void modify(int u, int l, int r, int k) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].lazy = k;
cal_lazy(tr[u], tr[u]);
return ;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid)
modify(lc, l, r, k);
if (r > mid)
modify(rc, l, r, k);
pushup(tr[u], tr[lc], tr[rc]);
}
Node query(int u, int l, int r) { //区查
if (l <= tr[u].l && tr[u].r <= r)
return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if (r <= mid)
return query(lc, l, r);
if (l > mid)
return query(rc, l, r);
Node U{l, r};
Node L = query(lc, l, r), R = query(rc, l, r);
pushup(U, L, R);
return U;
}
};
struct Node { //线段树定义
int l, r;
Z lazy, mul, res;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
SegmentTree<Node> seg(a);
while (q--) {
int op;
cin >> op ;
if (op == 1) {
int l, r, x;
cin >> l >> r >> x;
seg.modify(1, l, r, x);
} else {
int l, r;
cin >> l >> r;
cout << seg.query(1, l, r).res << "\n";
}
}
return 0;
}