Educational Codeforces Round 132 (Rated for Div

Educational Codeforces Round 132 (Rated for Div. 2)

Recover an RBS

给你一个括号序列,里面存在?号,题目保证至少有一种方案使得该括号序列合法,那么你能够替换?为(和),问你方案是否唯一

题解:思维 : 好题目,有个引理需要知道

我们知道能够形成一个\(RBS\),n一定是偶数,而且题目中已经给出至少存在一种合法方案,但是做这道题我们需要知道一个重要的引理:

对于任何一个合法的括号序列s,它的任意位置前(包括该位置)左括号的数量一定大于等于右括号的数量

那么我们直接考虑最优的情况,也就是我们尽可能使得位置前面的右括号尽可能的少,所以最优的情况就是使得前\(n/2\)的位置都是'()',后面\(n/2\)的位置上都是')',例如:\(((()))\),那么我们可以将所有?都替换成(,直到(的数量达到n/2,然后再将?替换成)

上面我们说的是最优的情况,那么题目已经说明了这种情况一定存在,那么我们只需要检查一下次优的方案能不能成立即可,我们只需要交换最后一个?替换的( 和第一个?替换的 ) 即可,如果这是一个合法的括号序列,那么说明方案不唯一,否则唯一,检查合法性的方法上面的引理已经给出

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

void solve()
{
    string s;
    cin >> s;
    int n = s.length();
    s = " " + s;
    int cntL = 0, cntR = 0;
    for (int i = 1; i <= n; ++i)
    {
        if (s[i] == '(')
            cntL++;
        else if (s[i] == ')')
            cntR++;
    }
    int pos1 = -1, pos2 = -1;
    int ishead = 1;
    for (int i = 1; i <= n; ++i)
    {
        if (s[i] == '?' && cntL < n / 2)
        {
            cntL++;
            s[i] = '(';
            pos1 = i;
        }
        else if (s[i] == '?' && cntR < n / 2)
        {
            if (ishead == 1)
                pos2 = i;
            ishead = 0;
            cntR++;
            s[i] = ')';
        }
    }
    if (pos1 == -1 || pos2 == -1)
    {
        cout << "YES" << endl;
        return;
    }
    int ok = 1;
    swap(s[pos1], s[pos2]);
    cntL = 0, cntR = 0;
    for (int i = 1; i <= n; ++i)
    {
        if (s[i] == '(')
            cntL++;
        else if (s[i] == ')')
            cntR++;
        if (cntR > cntL)
        {
            ok = 0;
            break;
        }
    }
    if (ok)
        cout << "NO" << endl;
    else
        cout << "YES" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Rorororobot

给你一张行宽为n,列宽为m的矩形地图,每一列有从第1行到第\(a_i\)行的障碍,你是一个机器人,只能上下左右的移动,并且不能穿过障碍和越界,但是每次给你的指令必须执行k次,也就是说必须每次步长为k,现在给你多次询问,每次询问给出一个起点和终点,以及步长k,让你回答能否从起点到达终点

题解:线段树维护区间最大值

很明显,如果我们不能直接从起点到达终点,即中间路段有障碍,那我们必须要先走到能走到的行最远的位置,因为我们需要绕路,然后我们现在处于能走到的行最远的位置,我们只需要求出终点和起点之间的列中最大的\(a_i\)即可,存在\(1e5\)次询问,我们只需要用数据结构加速即可,这里我们选择线段树;

那么如果我们需要绕路只需要判断两点即可:

  1. 起点和终点之间的列有没有比最远的\(sx\)要大的,如果存在,肯定是过不去的
  2. 如果能到终点那一列,我们考虑曼哈顿x轴距离和曼哈顿y轴距离是否都能够整除k即可
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

struct node
{
    int maxx;
} seg[N << 2];
int n, m, q;
int a[N];

void up(int id)
{
    seg[id].maxx = max(seg[lson].maxx, seg[rson].maxx);
}
void build(int id, int l, int r)
{
    if (l == r)
    {
        seg[id].maxx = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    up(id);
}

int query(int id, int l, int r, int ql, int qr)
{
    if (ql <= l && r <= qr)
    {
        return seg[id].maxx;
    }
    int mid = l + r >> 1;
    if (qr <= mid)
        return query(lson, l, mid, ql, qr);
    else if (ql > mid)
        return query(rson, mid + 1, r, ql, qr);
    else
        return max(query(lson, l, mid, ql, qr), query(rson, mid + 1, r, ql, qr));
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= m; ++i)
        cin >> a[i];
    build(1, 1, m);
    cin >> q;
    while (q--)
    {
        int sx, sy, ex, ey, k;
        cin >> sx >> sy >> ex >> ey >> k;
        if (sy > ey)
        {
            swap(sx, ex);
            swap(sy, ey);
        }
        sx = sx + (n - sx) / k * k;
        if (query(1, 1, m, sy, ey) >= sx)
        {
            cout << "NO" << endl;
            continue;
        }
        if (abs(ex - sx) % k != 0 || abs(ey - sy) % k != 0)
            cout << "NO" << endl;
        else
            cout << "YES" << endl;
    }
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

XOR Tree

给定一颗树,每个节点都有点权,现在你需要让树上任意两点之间的简单路径上的点权异或和不为0,你可以改变任意一点的点权,问最少改变几次使得所有简单路径点权异或和不为0?

题解:树形DP+异或+最近公共祖先

首先假设\(dis[u]\)代表从根节点出发到u节点简单路径上的异或和,那儿类似u和v之间的简单路径的长度,肯定经过他们的最近公共祖先,所以我们根据异或的性质和类比u和v之家的路径长度,我们得出u和v之间简单路径的异或和:\(dis[u]\bigoplus dis[v]\bigoplus a[lca(u,v)]\)

那么我们对每个点开一个\(set\)(集合),集合中存放它的子树中到根节点路径的异或和,那么我们只要做个自下而上树形dp就好了

也就是说我只要在以u节点为根的两个子树的集合内发现异或和为0,那么我们就必须修改\(a[u]\),只要改成无穷大即可,那么如果我们改成无穷大后,也就意味着u节点的存放的集合,也就是存放其子树中到根节点的路径异或和的集合,这里面的数永远不会和其他节点的子树中的路径异或和变为0,也就是说我们可以直接清空\(st[u]\)

注意两点:

  1. 该题卡常,我们在树形dp时必须优先遍历小的集合,否则\(TLE\)
  2. 我们必须及时清空子树中的集合,否则\(MLE\)
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n;
int a[N];
set<int> st[N];
vector<int> g[N];
int dis[N];
int ans;

void dfs(int u, int par)
{
    dis[u] = dis[par] ^ a[u];
    st[u].insert(dis[u]);
    bool ok = false;
    for (auto v : g[u])
    {
        if (v == par)
            continue;
        dfs(v, u);
        if (st[v].size() > st[u].size())	//优先小的
            st[u].swap(st[v]);
        for (auto x : st[v])
        {
            if (st[u].count(x ^ a[u]))
            {
                ok = true;
                break;
            }
        }
        for (auto x : st[v])
            st[u].insert(x);
        st[v].clear();		//及时清空子树中的集合
    }
    if (ok == true)
    {
        st[u].clear();		//无后效性,直接不用管u的子树了
        ans++;
    }
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 1, u, v; i < n; ++i)
    {
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
posted @ 2023-03-07 00:49  Zeoy_kkk  阅读(19)  评论(0编辑  收藏  举报