abc 380 D(思维?)E(并查集维护信息)

D 题意:
对于给定的字符串s
定义一次变换 :s=s+change(s)
change s 是将s 中的大写字母变为小写字母,小写字母变为大写字母。
对这个字符串进行 10^100 次变换
有Q次询问,询问第 k 位的字母是什么

对于len=2 的字符串 ,
2
第一次 2+2
4+4
8+8
对于一个位置的字符,我们考虑他是由上一轮中的那一个位置的字符转换而来。一直递归找到我在初始的字符中的位置。
所以我么可以找到 这个字符 是由我一开始字符串中的那一个字符变过过来的。(其实就是 k - 上一轮的长度)

K最大是 1e18 ,因为我的字符串的长度是2 ^(轮数),所以我当前递归找的这个 是对长度取log 的。所以每一次询问的时间复杂度 接近60次。复杂度是 Q *60
这个题 在做的时候,一直在找啥规律。呃呃,属实是被简单题薄纱了

#include <bits/stdc++.h>
using namespace std;
#define int long long 
string s;
char change(char ch)
{
    return ch>='a'?(ch-32):(ch+32);
}
char solve(int x,bool is_reverse)
{
	if (x<=s.size())
    {
        if (is_reverse) return  change(s[x-1]);
        else return s[x-1];
    }
    int k=s.size();
    while(k*2<x)k*=2;
    return solve(x-k,!is_reverse);
}

signed main()
{
    std::cin.tie(nullptr)->sync_with_stdio(false);
    cin>>s;
    int q;cin>>q;
    while(q--)
    {
        int pos;cin>>pos;
        cout<<solve(pos,false)<<" ";
    }
   
    return 0;
}


E 题意:
能显然的看出来 是并查集。具体怎么维护信息当时没有细想。还是写题写的太少了,也是没有怎么好好想。
现在看来也是没啥思维量的题。我每次的修改之后,我只需要查询我当前集合的左边和右边集合的颜色。决定是否合并。

对于每一个集合 我们需要记录这个集合的左边界和右边界。因为这样我们才能知道这个集合相邻的集合的信息。
根据每一个集合的l r ,我们可以求得 这个集合中元素的数量,当然我们也可以直接维护好每个集合的cnt

#include <bits/stdc++.h>
using namespace std;
int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * f;
}

void solve()
{
    int n, q;
    cin >> n >> q;
    vector<int> fa(n + 2);
    vector<int> l(n + 2);
    vector<int> r(n + 2);
    vector<int> cnt(n + 2); // 每一个集合的大小
    vector<int> col(n + 2); // 每个集合的颜色
    vector<int> ans(n + 2); // 每一个颜色的个数
    auto find = [&](auto &&self, int x) -> int
    {
        return x == fa[x] ? x : (fa[x] = self(self, fa[x]));
    };
    auto fun = [&](int x, int c) -> void
    {
        x = find(find, x);
        ans[col[x]] -= cnt[x];
        ans[c] += cnt[x];
        col[x] = c;
    };
    auto unit = [&](int x, int y)
    {
        // 将这两个合并
        int fx = find(find, x);
        int fy = find(find, y);
        if (fx != fy)
        {
            fa[fx] = fy;
            l[fy] = min(l[fx], l[fy]);
            r[fy] = max(r[fx], r[fy]);
            cnt[fy] += cnt[fx];
        }
    };
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        l[i] = i;
        r[i] = i;
        cnt[i] = 1;// 这个集合中的元素数量
        ans[i] = 1;
        col[i] = i;
    }
    
    while (q--)
    {
        int op;
        cin >> op;
        if (op == 1)
        {
            int x, c;
            cin >> x >> c;
            fun(x, c);
            int fl = find(find, l[find(find, x)] - 1);
            int fr = find(find, r[find(find, x)] + 1);
            if (col[fl] == c)
                unit(x, fl);
            if (col[fr] == c)
                unit(x, fr);
        }
        else
        {
            int c;
            cin >> c;
            cout << ans[c] << "\n";
        }
    }
}
int main()
{
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int t = 1;
    // cin>>t;
    while (t--)
    {
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值