牛客小白月賽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;
}