The 2022 ICPC Asia Regionals Online Contest

第一场A题A 01 Sequence

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题意:
有一个长度为n的首尾相接的01串,支持一种操作,选中si(si==1),将si和si左右两边的字符删除掉(一共删除三个字符),删除后相邻元素自动贴近,有q次询问,每次询问区间l,r(区间l,r也是首尾相接,并且保证区间长度是3的倍数),问最小需要将多少的0改成1才能把区间l,r全部删除干净。
分析:
计算操作数。
对于一个长度为len的区间,需要进行len / 3次操作才能删除干净(len一定是3的倍数),这是需要进行的操作数。
计算当前区间最多可支持的操作数sum,最后答案就是max(0, len/3 - sum)。
对于连续的k个1,最多进行k/2向上取整次操作,由此可计算出当前区间最多可支持的操作数。
解法有两种,第一种是预处理左右连续的1的个数和可操作数的前缀和,之后o(1)回答。第二种是直接利用线段树维护,线段树主要维护4个变量,区间内最左/右侧连续的1,除了最左/右连续的1之外,还可支持的操作数,区间是否全为1。
官方题解:
在这里插入图片描述
AC代码:
预处理左右连续的1的个数和可操作数的前缀和:

#include <bits/stdc++.h>

using namespace std;
#define io ios::sync_with_stdio(false)

int n, q;
string s;
int sum[1000050];
int pre[1000050];
int last[1000050];
int cnt, tmp, st, num;
int l, r;

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    cin >> n >> q;
    cin >> s;

    s = "!" + s;
    for(int i = 1; i <= n; ++i)
    {
        if(s[i] == '1') ++cnt;
        else cnt = 0;
        sum[i] = sum[i - cnt - 1] + (cnt + 1) / 2;
        pre[i] = cnt;
    }

    cnt = 0;

    for(int i = n; i >= 1; --i)
    {
        if(s[i] == '1') ++cnt;
        else cnt = 0;
        last[i] = cnt;
    }

    while(q--)
    {
        cin >> l >> r;
        if(last[l] >= r)
        {
            cout << 0 << '\n';
            continue;
        }
        num = (last[l] + pre[r] + 1) / 2;
        st = (r - l + 3) / 3;
        num += sum[r - pre[r]] - sum[l + last[l]];
        cout << max(0, st - num) << '\n';
    }

    return 0;
}

线段树维护:

#include <bits/stdc++.h>

using namespace std;

char ch[1000050];
int n, q;
int ar[1000050];
struct node
{
    int l, r;
    int numl, num, numr;
    bool flag;
}tree[4000050];
int l, r;
int st, sum;

int calc(int x)
{
    return (x + 1) >> 1;
}

node merge_node(const node &a, const node &b)
{
    if(a.flag)
    {
        if(b.flag) return {a.l, b.r, a.numl + b.numl, 0, a.numr + b.numr, 1};
        else return {a.l, b.r, a.numl + b.numl, b.num, b.numr, 0};
    }
    else
    {
        if(b.flag) return {a.l, b.r, a.numl, a.num, a.numr + b.numr, 0};
        else return {a.l, b.r, a.numl, a.num + b.num + calc(a.numr + b.numl), b.numr, 0};
    }
}

void build(int p, int l, int r)
{
    if(l == r)
    {
        if(ar[l]) tree[p] = {l, r, 1, 0, 1, 1};
        else tree[p] = {l, r, 0, 0, 0, 0};
        return ;
    }

    int mid = (l + r) >> 1;
    build(p<<1, l, mid);
    build(p<<1|1, mid + 1, r);

    tree[p] = merge_node(tree[p<<1], tree[p<<1|1]);
}

node query(int p, int l, int r)
{
    if(l <= tree[p].l && tree[p].r <= r) return tree[p];

    int mid = (tree[p].l + tree[p].r) >> 1;
    if(r <= mid) return query(p<<1, l, r);
    else if(l > mid) return query(p<<1|1, l, r);
    node res1 = query(p<<1, l, r);
    node res2 = query(p<<1|1, l, r);
    return merge_node(res1, res2);
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    cin >> n >> q;

    cin >> ch + 1;
    for(int i = 1; i <= n; ++i) ar[i] = ch[i] ^ 48;

    build(1, 1, n);

    while(q--)
    {
        cin >> l >> r;
        node res = query(1, l, r);

        st = (r - l + 3) / 3;
        sum = res.num + calc(res.numl + res.numr);

        cout << max(0, st - sum) << '\n';
    }

    return 0;
}

第二场A题Yet Another Remainder

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题意:
有一个长度为n数字,求这个数字模p的值。
这个数字的每一位用bi表示。告诉你min(n,100)行数据,第i行i个数字,第1行一个数字表示b1+b2+…+bn的值,第二行两个数字分别表示b1+b3+b5+…,b2+b4+b6+…,第三行三个数字分别表示b1+b4+b7+…,b2+b5+b8+…,b3+b6+b9+…。以此类推
分析:
答案是从1~n求和 bi*10^(n-i) 后%p
10^(n-i)%p是按照一定规律循环出现的,把%p后相同的数字提出来,剩下的事一坨bi的相加,可以发现,对于每个%p后的数字要成的那一坨bi的累加正好就是题干输入的某一行,找出计算即可。

AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int t, n, q, x;
vector<int> vt[105];
vector<int> tmp;
ll ans;

ll pow_mod(ll a, ll n, ll mod)
{
    ll ans = 1;
    a %= mod;
    while(n)
    {
        if(n & 1) ans = (ans * a) % mod;
        a = (a * a) % mod;
        n >>= 1;
    }
    return ans;
}

void work_1()
{
    while(q--)
    {
        scanf("%d", &x);
        int bas = 1;
        ans = 0;
        for(int i = n - 1; i >= 0; --i)
        {
            ans = (ans + (vt[n][i] * bas) % x) % x;
            bas = bas * 10 % x;
        }
        printf("%lld\n", ans);
    }
}

void work_2()
{
    while(q--)
    {
        scanf("%d", &x);

        ans = 0;
        tmp.clear();
        tmp.push_back(pow_mod(10, n - 1, x));

        int md;
        for(int i = 2; i < n; ++i)
        {
            md = pow_mod(10, n - i, x);
            //cout << i << ' ' << md << '\n';
            if(md == tmp[0]) break;
            else tmp.push_back(md);
        }

        int sum = tmp.size();

        //cout << sum << '\n';
        //for(int i = 0; i < sum; ++i) cout << tmp[i] << '\n';

        for(int i = 0; i < sum; ++i)
        {
            ans = (ans + (vt[sum][i] * tmp[i]) % x) % x;
        }

        printf("%lld\n", ans);
    }
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);

        for(int i = 0; i <= 100; ++i) vt[i].clear();

        for(int i = 1; i <= min(n, 100); ++i)
        {
            for(int j = 1; j <= i; ++j)
            {
                scanf("%d", &x);
                vt[i].push_back(x);
            }
        }

        scanf("%d", &q);

        if(n <= 100) work_1();
        else work_2();
        //work_2();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值