ACM___________________________

______________白白の屋
posts - 182, comments - 102, trackbacks - 0, articles - 0

MiYu原创, 转帖请注明 : 转载自 ______________白白の屋    

 

题目地址:

http://acm.hdu.edu.cn/showproblem.php?pid=1540

题目描述:

Tunnel Warfare

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1009    Accepted Submission(s): 334


Problem Description
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!
 

Input
The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.

There are three different events described in different format shown below:

D x: The x-th village was destroyed.

Q x: The Army commands requested the number of villages that x-th village was directly or indirectly connected with including itself.

R: The village destroyed last was rebuilt.
 

Output
Output the answer to each of the Army commanders’ request in order on a separate line.
 

Sample Input
7 9 D 3 D 6 D 5 Q 4 Q 5 R Q 4 R Q 4
 

Sample Output
1 0 2 4
 

 题目分析:

题目有三种操作 :

D:  摧毁村庄

Q: 查询相连的村庄

R: 修复上次被摧毁的村庄

这个题目的关键部分就是 对线段的修改部分, 也是最难的部分, 这部分理解了, 这个题目就基本会了.

  在结构体里面, 需要保存 3个量, lVal : 从线段左端点能够向右延伸的最大长度,

     rVal:从线段右端点能够向左延伸的最大长度,

    mVal:当前线段的最大连续长度

         seg[rt].mVal = max ( seg[LL].rVal + seg[RR].lVal, max ( seg[LL].mVal, seg[RR].mVal ) ); //当前节点的最大连续长度 

         seg[rt].lVal = seg[LL].lVal + ( seg[LL].cov() ? seg[RR].lVal : 0 );   //当前节点的左端点能够向右延伸的最大长度 

         seg[rt].rVal = seg[RR].rVal + ( seg[RR].cov() ? seg[LL].rVal : 0 );   //当前节点的右端点能够向左延伸的最大长度  

查询的时候也要注意几种 情况 :  1 :  就是当前整个线段

      2 :  横跨 左右子树

      3 :  只在 左 或 右 子树

详细请看代码注释. 

代码如下 : 

代码
/*
Mail to   : miyubai@gamil.com
Link      : http://www.cnblogs.com/MiYu  || http://www.cppblog.com/MiYu
Author By : MiYu
Test      : 1
Complier  : g++ mingw32-3.4.2
Program   : 1540
Doc Name  : Tunnel Warfare
*/
//#pragma warning( disable:4789 )
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <utility>
#include <queue>
#include <stack>
#include <list>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
using namespace std;
inline int max ( int &a, int &b ) {
       
return a > b ? a : b;       
}
struct segTree {
       
int left, right, lVal, rVal, mVal; //lVal: 从线段左端点能够向右延伸的最大长度
                                          
//rVal: 从线段右端点能够向左延伸的最大长度 
                                          
//mVal: 当前线段的最大连续长度 
       int mid () { return (left + right)>>1;}  
       
int dis () { return right-left+1; }  // 线段的长度 
       void prs (int flag) { lVal = rVal = mVal = flag ? 0 : dis(); }   //按flag标记处理线段 
       bool cov () { return mVal == dis(); }   // 当前线段是否被覆盖 , 即 这条线段上的点没有任何一点被破坏 
}seg[160000];
inline void creat ( int left, int right, int rt = 1 ) {
     
int LL = rt << 1, RR = rt << 1 | 1
     seg[rt].left = left;
     seg[rt].right = right;
     seg[rt].prs (0);
     
if ( left == right ) return
     
int mid = seg[rt].mid();      
     creat ( left, mid, LL );
     creat ( mid + 1, right, RR );     
}
inline void modify ( int pos, int flag, int rt = 1 ) {
     
int LL = rt << 1, RR = rt << 1 | 1
     
if ( seg[rt].left == seg[rt].right ) {
          seg[rt].prs ( flag ); return;      
     } 
     
int mid = seg[rt].mid();
     
if ( pos <= mid ) modify ( pos, flag, LL );
     
else modify ( pos, flag, RR );
     
// 经典部分: 
     seg[rt].mVal = max ( seg[LL].rVal + seg[RR].lVal, max ( seg[LL].mVal, seg[RR].mVal ) ); //当前节点的最大连续长度 
     seg[rt].lVal = seg[LL].lVal + ( seg[LL].cov() ? seg[RR].lVal : 0 );   //当前节点的左端点能够向右延伸的最大长度 
     seg[rt].rVal = seg[RR].rVal + ( seg[RR].cov() ? seg[LL].rVal : 0 );   //当前节点的右端点能够向左延伸的最大长度 
}
inline int query ( int pos, int rt = 1 ) {
    
if ( seg[rt].cov() || seg[rt].mVal == 0 || seg[rt].left == seg[rt].right )
        
return seg[rt].mVal;
    
int LL = rt << 1, RR = rt << 1 | 1, mid = seg[rt].mid();        
    
if ( pos <= mid ) {
         
if ( pos > mid - seg[LL].rVal )  // 查询节点在左孩子节点所处的位置 
             return seg[LL].rVal + query ( mid+1, RR ); // 如果在线段的右端, 则还需要查询右孩子节点的左端 
         else return query ( pos, LL );    //左端只需查询左端 
    } else {
         
if ( pos <= mid + seg[RR].lVal )  // 同上 
             return seg[RR].lVal + query ( mid, LL );
         
else return query ( pos, RR );   
    }

inline int _getint(int &n){  //输入加速 
 char c;for(c=getchar();!isdigit(c);c=getchar());for(;isdigit(c);c=getchar())n=n*10+c-'0';return n;
}
deque <int> st; // stack 没有clear() 杯具了 
int main ()
{
    
int N, M;
    
char ask[3];
    
while ( scanf ( "%d%d"&N, &M ) == 2 ) {
           creat ( 1, N );
           st.clear();
           
while ( M -- ) {
                  scanf ( "%s",ask ); N = 0;
                  
switch ( ask[0] ) {
                         
case 'D':   _getint (N);
                                     modify ( N, 1 );
                                     st.push_back ( N );
                                     
break;
                         
case 'Q':   _getint (N);          
                                     printf ( "%d\n",query ( N ) );        
                                     
break;
                         
case 'R':   if ( st.empty() ) break;
                                     modify ( st.back(), 0 );
                                     st.pop_back();
                  }
           }
    }
    
return 0;
}

 


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理