用树状数组和二分寻找连续的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;
}