【POJ】2481 Cows

题目链接:http://poj.org/problem?id=2481

接着上文,那天下午接着刷了几道树状数组的题目,仍旧觉得树状数组使用已不是难题,困难点在于如何分析问题,转化为树状数组,有些题目刚刚拿到时,都不会觉得这能用树状数组解决?! 比如说这个题目……(其实也是个水题)

后来灰溜溜回去上线问了merlininice师父,他起先没说什么,看完题目就开始分析:

 “ 如果我们按照每头牛的Ei值进行降序排序,接着按照重新排序后的牛开始数,找到第一头的时候先将他S1后面的都加1,找到第二头的时候,先看看他的S2的值是否为1:为1 的时候就说明第一头牛的区间有可能跨越他(还得额外判断E1==E2,S1==S2),然后再将S2后的也都加1……”

顿悟!

后来挨了一顿批。(虽然只是说了几句)

于是代码就跃然纸上了…… 

同时也更清晰地知道牛和菜的距离……

 

YY这么多后整理下该题的思路:

STEP1:对输入的数据按照结束点(Ei)进行排序。

STEP2:对排序后的点按照起始点(Si)先进行add(),接着统计从整段区间起始点到该点的和sum(Si),将和sum()-1 即可(因为事先进行了add()操作,目测先sum()再add()的话sum值即为其解)。

STEP3:输出即可。

为什么要排序?因为树状数组只能对前N项数组进行求和统计,那么当我们想要使用树状数组来解决题目的时候,就需要将问题转化为统计前N项的和的形式。对于这道题目,想要判断某个区间是否跨越另一个区间,我们如果按照Ei进行降序排序后,那么当我面遍历区间点的时候,如果发现add()之后sum()值大于1,说明有个区间的Sk在该区间起始点前(或者相等,针对这道题目,这种情况得特殊处理下),又因为区间按照Ei进行降序排列,那么之前的区间的Ei必然要大于(或者相等)该点,则该段区间必然被其他区间跨越了,至于有多少个跨越区间,只需要sum()-1即可。

如图举个实际例子:(我们已经将4个区间点按照Ei进行排序了)

遍历先从S1开始:add(s1,1), sum(s1)-1 = 0;

           S2      :add(s2,1), sum(s2)-1 = 0;

           S3      :add(s3,1), sum(s3)-1 = 2; (因为前面有s1和s2点值更新了s3点值,此刻s3的值为3)

           S4      :add(s4,1), sum(s4)-1 = 2; (理由同上,同时,因为s3在s4之后,那么add(s3,1)不会更新到s4)

于是,答案就出来了……

 

下来后自己写,但是第一、二次都TLE了,后来参考了网上的做法,就AC了……

TLE的代码:(告诫后人,qsort()用一次就够了…)

while(~scanf("%d",&m), m){
        memset(c,0,sizeof(c));
          for(i=1; i<=m; ++i)   {
              scanf("%d %d",&q[i].x,&q[i].y);
              q[i].x+=1;
              q[i].y+=1;
              q[i].id = i;
          }
          qsort(q+1,m,sizeof(q[0]),cmp);

          for(i=1; i<=m; ++i) {
                add(q[i].x,1);
                q[i].num =sum(q[i].x)-1;
                cout<<q[i].num<<endl;
                if(i>1 && q[i-1].y==q[i].y &&q[i-1].x==q[i].x ){
                      int k=i;
                      while(k>1 && q[k].x==q[k-1].x && q[k].y==q[k-1].y){
                            q[i].num--;
                            k--;
                      }
                }
          }
          qsort(q+1,m,sizeof(q[0]),cmp1);

          cout<<q[1].num;
          for(i=2; i<=m; ++i)   cout<<" "<<q[i].num;
          cout<<endl;
    }

 

AC的代码:

View Code
 1 while(~scanf("%d",&m), m){
 2         memset(c,0,sizeof(c));
 3         n=0;
 4           for(i=1; i<=m; ++i)   {
 5               scanf("%d %d",&q[i].x,&q[i].y);
 6               q[i].x+=1;
 7               q[i].y+=1;
 8               q[i].id = i;
 9               n=n>q[i].y ? n : q[i].y;
10           }
11           qsort(q+1,m,sizeof(q[0]),cmp);
12 
13           for(i=1; i<=m; ++i) {
14                 add(q[i].x,1);
15                 ans[q[i].id] =sum(q[i].x)-1;
16                 if(i>1 && q[i-1].y==q[i].y &&q[i-1].x==q[i].x ){
17                       int k=i;
18                       while(k>1 && q[k].x==q[k-1].x && q[k].y==q[k-1].y){
19                             ans[q[i].id] --;
20                             k--;
21                       }
22                 }
23           }
24           //qsort(q+1,m,sizeof(q[0]),cmp1);
25 
26           cout<<ans[1];
27           for(i=2; i<=m; ++i)   cout<<" "<<ans[i];
28           cout<<endl;
29     }

 

这道题目之后树状数组的概念就更清晰了,看来水水更健康也是没错的哈~~

posted on 2012-07-21 11:28  Yuna_  阅读(83)  评论(0)    收藏  举报