• 北京师范大学第十六届程序设计竞赛决赛--H 吾好梦中做题


    本以为只是不会线段树,写完之后发现二分也不会

    问题出在几个地方:

    一、

    这个是正确复杂度的查询

    int query(int x, int y, int p)
    {
        int l = t[p].l, r = t[p].r;
        if (!x)return 0;
        if (x <= l && r <= y)return t[p].vue;
        pushdown(p);
        int mid = l + r >> 1;
        if (y <= mid)return query(x, y, p << 1);
        else if (x > mid)return query(x, y, p << 1 | 1);
        else
            return min(query(x, y, p << 1), query(x, y, p << 1 | 1));
    }
    

    这个是会T的查询

    int query(int x, int y, int p)
    {
        if(!x)return 0;
        int l = t[p].l, r = t[p].r;
        if (x <= l && r <= y)return t[p].vue;
        pushdown(p);
        int mid = l + r >> 1;
        if (x <= mid && y > mid)return min(query(x, y, p << 1), query(x, y, p << 1 | 1));
        else if (x <= mid)return query(x, y, p << 1);
        else return query(x, y, p << 1 | 1);
    }
    

    就是在区间划分的时候

    if (y <= mid)return query(x, y, p << 1);
        else if (x > mid)return query(x, y, p << 1 | 1);
        else
            return min(query(x, y, p << 1), query(x, y, p << 1 | 1));
    

    这样写比之前的写法时间上更优


    二、

    二分答案出现问题,之前写的二分个人理解可能是由于mid下取整导致在处理这种问题

    上不够稳定

    二分最后一个满足条件的点(可能不存在)

    while (l <= r)
    {
    	int mid = l + r >> 1;
    	if (query(mid, r, 1) == limx)//如果有合法状态在区间内的话最小值必然等于limx
    		ans = mid, l = mid + 1;
    	else
    		r = mid - 1;//没有就换个区间
    }
    

    二分第一个满足条件的点(可能不存在)

    while (l <= r)
    {
    	int mid = l + r >> 1;
    	if (query(l, mid, 1) < limx)
    	{
    		ans = mid;
    		r = mid - 1;
    	}
    	else
    		l = mid + 1;
    }
    

    我们记录左括号为+1,右括号为-1,求一个前缀和,然后用线段树维护区间最小值

    题目要求找从某个点(x)出发的最长匹配,就是前缀和等于(pre[x-1])的点(i),并且满足(x<=j<=i)同时(pre[j]>=pre[x-1]),那么只有2种情况:

    ①、对于区间([x,n]),其中(x<=j<=n)且都满足(pre[j]>=pre[x-1])

    形如:()()()(()()() ) ((

    找到最后一个(pre[j]==pre[x-1])即为答案
    (但可能出现"((((((((((((("即(pre[j]>pre[x-1])这种特殊情况,需要将ans初始化为x-1,在二分更新l的时候更新答案,若不更新即为该种情况)

    ②、对于区间([x,n]),其中(x<=j<=n)存在(pre[j]<pre[x-1])

    就去寻找第一个(pre[j]<pre[x-1])(j),答案即为(j-x)

    剩下的都写在注释里了

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    //#include<unordered_map>
    using namespace std;
    #define ll long long
    #define Ls t[p << 1]
    #define Rs t[p << 1 | 1]
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    const int maxn = 5e5 + 10;
    const int inf = 1e9;
    ll mod = 1e9 + 7;
    
    char s[maxn];
    
    int n;
    
    struct tree {
        int l, r;
        int vue, lazy;
    }t[maxn << 2];
    
    int a[maxn];
    
    void build(int l, int r, int p)
    {
        t[p].lazy = 0;
        t[p].l = l, t[p].r = r;
        if (l == r)
        {
            t[p].vue = a[l];
            return;
        }
        int mid = l + r >> 1;
        build(l, mid, p << 1);
        build(mid + 1, r, p << 1 | 1);
        t[p].vue = min(Ls.vue, Rs.vue);
    }
    
    void pushdown(int p)
    {
        if (t[p].lazy)
        {
            int lazy = t[p].lazy;
            Ls.vue += lazy;
            Rs.vue += lazy;
            Ls.lazy += lazy;
            Rs.lazy += lazy;
            t[p].lazy = 0;
        }
    }
    
    void change(int x, int y, int k, int p)
    {
        int l = t[p].l, r = t[p].r;
        if (x <= l && r <= y) {
            t[p].vue += k;
            t[p].lazy += k;
            return;
        }
        pushdown(p);
        int mid = l + r >> 1;
        if (x <= mid)change(x, y, k, p << 1);
        if (y > mid)change(x, y, k, p << 1 | 1);
        t[p].vue = min(Ls.vue, Rs.vue);
    }
    
    int query(int x, int y, int p)
    {
        int l = t[p].l, r = t[p].r;
        if (!x)return 0;
        if (x <= l && r <= y)return t[p].vue;
        pushdown(p);
        int mid = l + r >> 1;
        if (y <= mid)return query(x, y, p << 1);
        else if (x > mid)return query(x, y, p << 1 | 1);
        else
            return min(query(x, y, p << 1), query(x, y, p << 1 | 1));
    }
    
    int main()
    {
        //freopen("C:\1.in", "r", stdin);
        //fastio;
        int t;
        scanf("%d", &t);
        while (t--)
        {
            int q;
            scanf("%d%d", &n, &q);
            scanf("%s", s + 1);
            for (int i = 1; i <= n; i++)
                a[i] = a[i - 1] + (s[i] == '(' ? 1 : -1);
            build(0, n, 1);
            while (q--)
            {
                int o, x;
                scanf("%d%d", &o, &x);
                if (o == 1)
                {
                    if (s[x] == '(')
                    {
                        s[x] = ')';
                        change(x, n, -2, 1);
                    }
                    else
                    {
                        s[x] = '(';
                        change(x, n, 2, 1);
                    }
                }
                else
                {
                    int limx = query(x - 1, x - 1, 1), MIN = query(x, n, 1), l = x, r = n;
                    //cout << limx << " " << MIN << endl;
                    if (MIN >= limx)//在匹配段后没有出现")..."这种情况,即')'全被匹配,'('多了
                    {
                        //寻找最后一个等于limx的位置
                        int ans = x - 1;
                        while (l < r)
                        {
                            int mid = l + r >> 1;
                            if (query(mid, r, 1) == limx)//如果有合法状态在区间内的话最小值必然等于limx
                                ans = mid, l = mid + 1;
                            else
                                r = mid - 1;//没有就换个区间
                        }
                        //cout << l << " " << x << endl;
                        printf("%d
    ", ans - x + 1);
                    }
                    else//在合法状态之后会出现')'的直接导致不能继续延展
                    {
                        //寻找第一个不合法位置
                        int ans;
                        while (l <= r)
                        {
                            int mid = l + r >> 1;
                            //cout << query(x, mid, 1) << " " << limx << endl;
                            if (query(l, mid, 1) < limx)
                            {
                                ans = mid;
                                r = mid - 1;
                            }
                            else
                                l = mid + 1;
                        }
                        printf("%d
    ", ans - x);
                        //cout << endl;
                    }
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Unity编译Android的原理解析和apk打包分析
    TokuDB性能测试报告
    一起脱去小程序的外套和内衣
    go单元测试进阶篇
    小程序发布后最全解析!
    部署在腾讯云的公益网站遭受了一次CC攻击
    基于Http原理实现Android的图片上传和表单提交
    腾讯云TCCE培训认证 精彩的第一次
    【腾讯云的1001种玩法】在腾讯云上创建您的SQL Cluster(5)
    腾讯云容器服务的滚动升级使用简介
  • 原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/13521859.html
Copyright © 2020-2023  润新知