用樹狀數組和二分尋找連續的1.http://acm.hdu.edu.cn/showproblem.php?pid=4339
做題過程:
開始做的時候發現自己以前做過。索性做吧。神奇的是我發現我以前用樹狀數組和線段是各A了一遍。真是勤奮的孩子。
我對這題的二分很是反感,記得以前做的時候就二分了很久來著,而且只是二分不會。。。。。。
對於線段樹的作法我已經淡忘了,只記得樹狀數組的。明天來把線段樹的再一寫。
今天我才意識到,單點更新這東西是更新到底層,複雜度很高的,沒有什麼優越性。
/*Pro: 0Sol: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 ) >> 1using 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;}