During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.
Frequently the invaders launched attack on some of the villages and destroyed the parts of tunnels in them. The Eighth Route Army commanders requested the latest connection state of the tunnels and villages. If some villages are severely isolated, restoration of connection must be done immediately!
题意是有一个线段上排列着很多村庄,现在有三个操作,一个是炸毁一个村庄,是它与两边都不连通,一个是修复一个村庄,还有一个是查询某个村庄,它向左右两边一共可以连通多少村庄。
虽然同样是线段树,网上很多题解都写的用线段树节点记录左边连续的长度和右边连续的长度,我刚学习线段树的时候因为不熟悉线段树的思路,也就照着理解了写的,但是我一直觉得这种做法很麻烦,容易写错,大概网上都是互抄博客吧。
我的思路是查询一个村庄能连通多少村庄,其实就是查询它左右两边最近的一个被毁掉的村庄分别是哪个,比如 5 号村庄,离它最近的被毁掉的村庄分别是 3 号和 8 号,那么它连通的就是 4~7 号村庄,就是(8-3-1)个村庄了。那么我就是让被毁的村庄显示在线段树里,我只要能够查询到它,就可以解决问题。比如离 5 号村庄最近的两个村庄应该是什么样的:1~4 号中被摧毁且编号最大的,与 6~n 号中被摧毁且编号最小的。那么我其实只要求两个区间内的最大与最小值就行了,这就可以写最基础的单点修改、区间求最值的线段树。
具体方法是:
首先最大值树中初始所有值都是-1,每摧毁一个点,就在它的位置上更新成它的编号,如 摧毁 3 号点,就将 3 这个位置更新成 3,这样被摧毁的点在线段树上的值是它的编号,可以用于直接查询最值,而没有被摧毁的则是 -1,不会影响。修复村庄则是更新回-1。
最小值树则相反,初始值是一个极大值(我用的是0x3f3f3f3f),摧毁点同样更新成编号,修复同样更新回极大值。
然后查询就直接查左边区间的最大值和右边区间的最小值就可以了。
细节是:
由于有某一边还没有村庄被摧毁,所有都是-1或极大值,这样需要特判。为了巧妙解决这个问题,我就在一开始初始化时,顺便摧毁 0 号点和 n+1号点,这两个不存在的点,这样查询的时候就无需特判也能直接这么做了。
当然,树状数组也可以完成上述的操作。
1 #include<stdio.h> 2 #include<string.h> 3 const int maxm=5e4+5; 4 const int INF=0x3f3f3f3f; 5 6 int ma[maxm<<2],mi[maxm<<2]; 7 int sta[maxm],cnt; 8 int maxx,minn; 9 char s[10]; 10 11 int max(int a,int b){return a>b?a:b;} 12 int min(int a,int b){return a<b?a:b;} 13 14 void update(int o,int l,int r,int ind,int c){ 15 if(l==r){ 16 if(c==1){ 17 ma[o]=-1; 18 mi[o]=INF; 19 } 20 else ma[o]=mi[o]=l; 21 return; 22 } 23 int m=l+((r-l)>>1); 24 if(ind<=m)update(o<<1,l,m,ind,c); 25 else update(o<<1|1,m+1,r,ind,c); 26 ma[o]=max(ma[o<<1],ma[o<<1|1]); 27 mi[o]=min(mi[o<<1],mi[o<<1|1]); 28 } 29 30 void query1(int o,int l,int r,int ql,int qr){ 31 if(ql<=l&&qr>=r){ 32 maxx=max(maxx,ma[o]); 33 return; 34 } 35 int m=l+((r-l)>>1); 36 if(ql<=m)query1(o<<1,l,m,ql,qr); 37 if(qr>=m+1)query1(o<<1|1,m+1,r,ql,qr); 38 } 39 40 void query2(int o,int l,int r,int ql,int qr){ 41 if(ql<=l&&qr>=r){ 42 minn=min(minn,mi[o]); 43 return; 44 } 45 int m=l+((r-l)>>1); 46 if(ql<=m)query2(o<<1,l,m,ql,qr); 47 if(qr>=m+1)query2(o<<1|1,m+1,r,ql,qr); 48 } 49 50 int main(){ 51 int n,m; 52 while(scanf("%d%d",&n,&m)!=EOF){ 53 cnt=0; 54 memset(ma,-1,sizeof(ma)); 55 memset(mi,0x3f,sizeof(mi)); 56 update(1,0,n+1,0,0); 57 update(1,0,n+1,n+1,0); 58 for(int i=1;i<=m;++i){ 59 scanf("%s",s); 60 if(s[0]==‘D‘){ 61 int ind; 62 scanf("%d",&ind); 63 update(1,0,n+1,ind,0); 64 sta[++cnt]=ind; 65 } 66 else if(s[0]==‘Q‘){ 67 int ind; 68 scanf("%d",&ind); 69 maxx=-1; 70 minn=INF; 71 query1(1,0,n+1,0,ind); 72 query2(1,0,n+1,ind,n+1); 73 if(minn==maxx)printf("0\n"); 74 else printf("%d\n",minn-maxx-1); 75 } 76 else if(s[0]==‘R‘){ 77 update(1,0,n+1,sta[cnt--],1); 78 } 79 } 80 } 81 return 0; 82 }