hdu - 4339 - Query - 树状数组||线段树

用树状数组和二分寻找连续的1.http://acm.hdu.edu.cn/showproblem.php?pid=4339

做题过程:

        开始做的时候发现自己以前做过。索性做吧。神奇的是我发现我以前用树状数组和线段是各A了一遍。真是勤奋的孩子。

        我对这题的二分很是反感,记得以前做的时候就二分了很久来着,而且只是二分不会。。。。。。

        对于线段树的作法我已经淡忘了,只记得树状数组的。明天来把线段树的再一写。

今天我才意识到,单点更新这东西是更新到底层,复杂度很高的,没有什么优越性。

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <vector>
using namespace std;
char str[2][1000009],d[10];
int Q,t,op,a,b,len,c[1000009];
void modify(int pos, int val){
    while(pos <= len){
        c[pos] += val;
        pos += (pos & -pos);
    }
}
int getsum(int pos){
    int sum = 0;
    while(pos){
        sum += c[pos];
        pos -= (pos & -pos);
    }return sum;
}
int bin(int low, int high){
    int mid; int sum = getsum(low - 1),index = low;
    int ans = -1;
    while(low <= high){
        mid = (low + high) >> 1;
        if(getsum(mid) - sum == mid - index + 1){
            low = mid + 1;
            ans = max(ans,mid - index + 1);
        }
        else high = mid - 1;
    }
    return ans;
}
int main(){
    scanf("%d",&t);
    for(int ca = 1; ca <= t; ca ++){
        printf("Case %d:\n",ca);
        memset(str[0],0,strlen(str[0]));
        memset(str[1],0,strlen(str[1]));
        scanf("%s%s",str[0],str[1]);
        int len1 = strlen(str[0]);
        int len2 = strlen(str[1]);
        len = min(len1,len2);   //
        memset(c,0,sizeof(c));
        for(int i = 0; i < len; i ++){
            if(str[0][i] == str[1][i])
                modify(i + 1, 1);
        }
        scanf("%d",&Q);
        for(int i = 0; i < Q; i ++){
            scanf("%d",&op);
            if(op == 2){
                scanf("%d",&a);
                int tmp = bin(a + 1,len);
                if(tmp == -1)
                    puts("0");
                else
                    printf("%d\n",tmp);//a + 1后面有多少个连续的1
            }else{
                scanf("%d%d%s",&a,&b,d);
                a --;
                int fk = (str[0][b] == str[1][b]);
                str[a][b] = d[0];
                int fg = (str[a][b] == str[!a][b]);

                if(fk == 1 && fg == 0){//改前一样,改后不一样
                    modify(b + 1, -1);
                }else if(fk == 0 && fg == 1){//改前不一样,改后一样
                    modify(b + 1 ,1);
                }
            }
        }
    }
	return 0;
}

贴了以前的代码,因为以前的注释多。仍是不懂l,r,p有什么不同。正确的写着倒是很自然,但是不明白错的为什么错了。。。

//思路: 单点更新,区间查询。
//线段树每个节点记录该区间从左向右最后一个相同的位置,若左边第一个就不相同,则记为-1
//这道题目的难点就是在于区间的合并的时候
#include <iostream>
#include <cstring>
#include <cstdio>
#define lson l,m, rt << 1
#define rson m+ 1, r, rt << 1 | 1
#define maxn 1000100
#define havem int m = (l + r ) >> 1
using namespace std;
int d[maxn << 2],Max;
char s1[maxn],s2[maxn];
void push_up(int rt , int m){
    if(d[rt << 1] == m){
        d[rt] = max(d[rt << 1] , d[rt << 1 | 1]);
    }else
        d[rt] = d[rt << 1];
}
void build(int l , int r, int rt){
    if(l == r){
        if(s1[l] == s2[r]) d[rt] = l;
        else d[rt] = -1;    return ;
    }
    int m = (l + r) >> 1;
    build(lson); build(rson);

    push_up(rt,m);
}
//单点更新
void update(int id, int p, char cc, int l , int r, int rt){
    if(l == r){
//        cout << p << l << r << endl;
        if(id == 1) s1[p] = cc;//修改,这里如果是l也错了。难道说递归到底层l,r,p还不一样?好像是一样的啊。。。无语了。。。。
        else s2[p] = cc;
        if(s1[l] == s2[r]) d[rt] = l;
        //这里一定要是l和r,如果是p,就wa了。why?
        else d[rt] = -1;    return ;
        //永远不要忘记return
    }
    havem;
    if(p <= m) update(id,p,cc,lson);
    else update(id,p,cc,rson);
    push_up(rt,m);
}
//区间询问
int query(int L, int R, int l , int r, int rt){
    if(L == l && R == r) return d[rt];
    int m = (l + r) >> 1 , ret1= -1, ret2 = -1;
    if(R <= m) return query(L,R,lson);
    if(L > m) return query(L,R,rson);

//执行到这里就是要查询的区间[L,R]覆盖[l,r]的中点了,这个时候需要注意了。
    ret1 = query(L,m,lson);

    if(ret1 == m) {
        ret2 = query(m + 1, R, rson);
        return max(ret1,ret2);
    }
    else return ret1;
}
int main(){
    int ca,Q,id,p,Max,cas = 1;
    char cc[4];
    scanf("%d",&ca);
    while(ca --){
        scanf("%s%s",s1,s2);
        scanf("%d",&Q);
        int len1 =strlen(s1), len2 = strlen(s2);
        Max = min(len1, len2) - 1;

        build(0,Max,1);
        printf("Case %d:\n",cas++);
        while(Q--){
            scanf("%d",&id);
            if(id == 1){
                scanf("%d %d %s",&id,&p,cc);
                update(id,p,cc[0],0,Max,1);
            }
            else{
                scanf("%d",&p);
                int ans = query(p,Max,0,Max,1);//ans返回的是位置
                if(ans == -1) printf("0\n");
                else printf("%d\n",ans - p + 1);
            }
         }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值