【NOI2008】 糖果雨

题目描述
糖果雨
【问题描述】
有一个美丽的童话:在天空的尽头有一个"糖果国",这里大到摩天大厦,小
到小花小草都是用糖果建造而成的。更加神奇的是,天空中飘满了五颜六色的糖
果云,很快糖果雨密密麻麻从天而落,红色的是草莓糖,黄色的是柠檬糖,绿色
的是薄荷糖,
黑色的是巧克力糖......这时糖果国的小朋友们便会拿出大大小小的
口袋来接天空中落下的糖果,拿回去与朋友们一起分享。
对糖果情有独钟的小 Z 憧憬着能够来到这样一个童话的国度。所谓日有所
思,夜有所梦,这天晚上小 Z 梦见自己来到了"糖果国"。他惊喜地发现,任何时
候天空中所有的云朵颜色都不相同,
不同颜色的云朵在不断地落下相应颜色的糖
果。
更加有趣的是所有的云朵都在做着匀速往返运动,
不妨想象天空是有边界的,
而所有的云朵恰好在两个边界之间做着往返运动。
每一个单位时间云朵向左或向
右运动一个单位,当云朵的左界碰到天空的左界,它会改变方向向右运动;当云
朵完全移出了天空的右界,它会改变方向向左运动。
我们不妨把天空想象为一个平面直角坐标系,而云朵则抽象为线段(线段可
能退化为点):
如上图,不妨设天空的左界为 0,右界为 len。图中共有 5 片云朵,其中标
号为 1 的云朵恰好改变方向向右运动,标号为 2 的云朵恰好改变方向向左运动。
忽略云朵的纵坐标,它们在运动过程中不会相互影响。
小 Z 发现天空中会不断出现一些云朵(某个时刻从某个初始位置开始朝某个
方向运动),而有的云朵运动到一定时刻就会从天空中消失,而在运动的过程中
糖果在不断地下落。小 Z 决定拿很多口袋来接糖果,口袋容量是无限的,但袋
口大小却是有限的。例如在时刻 T 小 Z 拿一个横坐标范围为[L, R]的口袋来接糖
果,如果[L, R]存在一个位置 x,该位置有某种颜色的糖果落下,则认为该口袋可
接到此种颜色的糖果。极端情况下,袋口区间可能是一个点,譬如[0,0]、[1,1],
但仍然可以接到相应位置的糖果。通常可以接到的糖果总数会很大,因而小 Z
想知道每一次(即拿出口袋的一瞬间)他的口袋可以接到多少种不同颜色的糖果。
糖果下落的时间忽略不计。
【输入格式】
输入文件 candy.in 的第一行有两个正整数 n, len,
分别表示事件总数以及天空
的“边界”
。
接下来 n 行每行描述一个事件,
所有的事件按照输入顺序依次发生。
每行的
第一个数 k(k = 1,2,3)分别表示事件的类型,分别对应三种事件:插入事件,
询问事件以及删除事件。输入格式如下:
事件类型
输入格式
1 Ti Ci Li Ri Di                  插入事件                         (天空中出现了一片云朵)
2 Ti Li Ri                           询问事件        (询问一个口袋可以接到多少种不同颜色的糖果)
3 Ti Ci                               删除事件                        (天空中一片云朵消失了)
说明
时刻 Ti,天空中出现了一片坐标范围为[Li, Ri],颜色为 Ci 的云朵,初始的时候云朵运动方向
为向左(Di = -1)或向右(Di = 1)。满足 0 ≤ Li ≤ Ri ≤ len,Di = -11。
数据保证任何时刻空中不会出现两片颜色相同的云朵。时刻 Ti,小 Z 用一个坐标范围为[Li, Ri]的大口袋去接糖果,询问可以接到多少种不同的糖果。满足 0 ≤ Li ≤ Ri ≤ len。
时刻 Ti,颜色为 Ci 的云朵从天空消失中。数据保证当前天空中一定存在一片颜色为 Ci 的云朵。
【输出格式】
对于每一个询问事件,输出文件 candy.out 中应包含相应的一行,为该次询
问的答案,即口袋可以接到多少种不同的糖果。
【输入样例】
10 10
1 0 10 1 3 -1
2100
2 11 0 10
2 11 0 9
1 11 13 4 7 1
2 13 9 9
2 13 10 10
3 100 13
3 1999999999 10
1 2000000000 10 0 1 1
【输出样例】
1
1
0
2
1
【样例说明】
共 10 个事件,包括 3 个插入事件,5 个询问事件以及 2 个删除事件。
时刻 0,天空中出现一片颜色为 10 的云朵,初始位置为[1, 3],方向向左。
时刻 1,范围为[0, 0]的口袋可以接到颜色为 10 的糖果(云朵位置为[0, 2])。
时刻 11,范围为[0,10]的口袋可以接到颜色为 10 的糖果(云朵位置为[10, 12])。
时刻 11,范围为[0, 9]的口袋不能接到颜色为 10 的糖果(云朵位置为[10, 12])。
时刻 11, 天空中出现一片颜色为 13 的云朵, 初始位置为[4, 7], 方向向右。
时刻 13,范围为[9, 9]的口袋可以接到颜色为 10(云朵的位置为[8, 10])和颜色为 13(云朵的位置为[6, 9])两种不同的糖果。
时刻 13,范围为[10, 10]的口袋仅仅可以接到颜色为 10 的一种糖果(云朵的位
置为[8, 10]),而不可以接到颜色为 13 的糖果(云朵的位置为[6, 9]),。
时刻 100, 颜色为 13 的云朵从天空中消失。
时刻 1999999999,颜色为 10 的云朵从天空中消失。
时刻 2000000000,天空中又出现一片颜色为 10 的云朵,初始位置为[0, 1],
方向向右。
【数据规模和约定】
对于所有的数据,0 ≤ Ti ≤ 2000000000,1 ≤ Ci ≤ 1000000。
数据保证{Ti}为非递减序列即 T1 ≤ T2 ≤ ... ≤ Tn-1 ≤ Tn。
对于所有的插入事件,令 Pi = Ri – Li,即 Pi 表示每片云朵的长度。
数据编号           n                    len                  pi
1                       20                   10                 <=len
2                      200                 100                <=len
3                     2000               1000               <=len
4                   100000               10                 <=len
5                   100000              100                <=2
6                   150000             1000               <=3
7                   200000             1000               <=3
8                   100000             1000               <=len
9                   150000             1000               <=len
10                 200000             1000               <=len              

 

题解

 

解法1:模拟
 1 (*
 2     *Problem:    NOI2008 糖果雨
 3     *Author :    Chen Yang
 4     *Time   :    2012.5.20
 5     *State  :    30分
 6     *Memo    :    模拟
 7 *)
 8 program candy;
 9 const maxn=200200;
10 type
11   ty1=record
12     t,c,l,r,d:longint;
13   end;
14 var
15   n,len,tot,i,j,x,l,r,t,ans:longint;
16   wind:array[0..maxn] of ty1;
17   del:array[0..maxn] of boolean;
18 //=======================
19 function check1(var l,r:longint):boolean;
20 var
21   t:longint;
22 begin
23   t:=r-l;
24   if r>len+t then
25   begin
26     r:=len+t-(r-len-t); l:=r-t;
27     exit(true);
28   end;
29   exit(false);
30 end;
31 //=======================
32 function check2(var l,r:longint):boolean;
33 var
34   t:longint;
35 begin
36   t:=r-l;
37   if l<0 then
38   begin
39     l:=0-l; r:=l+t;
40     exit(true);
41   end;
42   exit(false);
43 end;
44 //=======================
45 function check(x:longint):boolean;
46 var
47   lx,rx:longint;
48   can:boolean;
49 begin
50   lx:=wind[x].l+wind[x].d*((t-wind[x].t) mod (len shl 1));
51   rx:=wind[x].r+wind[x].d*((t-wind[x].t) mod (len shl 1));
52   //writeln(x,' ',lx,' ',rx,' ',wind[x].l,' ',wind[x].r);
53   can:=true;
54   while can do
55   begin
56     //writeln(x,' ',lx,' ',rx);
57     can:=false;
58     if check1(lx,rx) then can:=true;
59     if check2(lx,rx) then can:=true;
60   end;
61   //writeln(t,' ',lx,' ',rx,' ',l,' ',r);
62   exit(not ((lx>r)or(rx<l)));
63 end;
64 //=======================
65 begin
66   assign(input,'candy.in'); reset(input);
67   assign(output,'candy.out'); rewrite(output);
68   read(n,len);
69   for i:=1 to n do
70   begin
71     read(x);
72     case x of
73     1:begin
74         inc(tot);
75         read(wind[tot].t,wind[tot].c,wind[tot].l,wind[tot].r,wind[tot].d);
76       end;
77     2:begin
78         read(t,l,r); ans:=0;
79         for j:=1 to tot do
80         if not del[j] and check(j) then inc(ans);
81         writeln(ans);
82       end;
83     3:begin
84         read(t,l);
85         for j:=1 to tot do
86         if wind[j].c=l then del[j]:=true;
87       end;
88     end;
89   end;
90   close(input); close(output);
91 end.

 

解法2:二维树状数组(PASCAL)
 1 (*
 2         Problem:        NOI2008 糖果雨
 3     Author:        Chen Yang
 4     Time:        2012.5.25 8:29 pm
 5     State:        AC
 6     Memo:        二维树状数组、平行四边形变换统计
 7 *)
 8 program candy;
 9 const maxc=1000100;
10       maxm=4010;
11       maxn=2010;
12 var
13   n,len,m,i,p,t,l,r,c,d,q:longint;
14   s:array[0..1,0..maxn,0..maxm] of longint;
15   x,y:array[0..maxc] of longint;
16 //=================
17 procedure add(p,x,y,v:longint);
18 var
19   i:longint;
20 begin
21   inc(x); inc(y);
22   while x<maxn do
23   begin
24     i:=y;
25     while i<maxm do
26     begin
27       inc(s[p,x,i],v);
28       inc(i,i and -i);
29     end;
30     inc(x,x and -x);
31   end;
32 end;
33 //=================
34 function sum(p,x,y:longint):longint;
35 var
36   i:longint;
37 begin
38   if (x<0)or(y<0) then exit(0);
39   inc(x); inc(y); sum:=0;
40   if x>n then x:=n+1;
41   if y>m then y:=m+1;
42   while x>0 do
43   begin
44     i:=y;
45     while i>0 do
46     begin
47       inc(sum,s[p,x,i]);
48       dec(i,i and -i);
49     end;
50     dec(x,x and -x);
51   end;
52 end;
53 //=================
54 procedure updata(t,c,l,r,d:longint);            inline;
55 begin
56   x[c]:=(t-d*l+n) mod n; y[c]:=r-l;
57   add(0,x[c],y[c]+x[c],1);
58   add(1,x[c],y[c]-x[c]+n,1);
59 end;
60 //=================
61 procedure change(c:longint);                    inline;
62 begin
63   add(0,x[c],y[c]+x[c],-1);
64   add(1,x[c],y[c]-x[c]+n,-1);
65 end;
66 //=================
67 function area(p,x1,y1,x2,y2:longint):longint;   inline;
68 begin
69   area:=sum(p,x2,y2)+sum(p,x1-1,y1-1)-sum(p,x1-1,y2)-sum(p,x2,y1-1);
70 end;
71 //=================
72 function ask(t,l,r:longint):longint;            inline;
73 var
74   d:longint;
75 begin
76   d:=longint(r=len);
77   ask:=area(0,t,l+t,t+r,m)+area(0,0,l+t-n,t+r-n-d,m)+
78        area(1,t-r+n+d,l-t,n,m)+area(1,t-r,l-t+n,t-1,m);
79 end;
80 //=================
81 begin
82   assign(input,'candy.in'); reset(input);
83   assign(output,'candy.out'); rewrite(output);
84   read(q,len); n:=len << 1; m:=len << 2;
85   for i:=1 to q do
86   begin
87     read(p);
88     case p of
89     1:begin read(t,c,l,r,d); updata(t,c,l,r,d);    end;
90     2:begin read(t,l,r); writeln(ask(t mod n,l,r)); end;
91     3:begin read(t,c); change(c); end;
92     end;
93   end;
94   close(input); close(output);
95 end.

 

解法2:二维树状数组(C++)
 1 /*
 2         problem:        NOI2008 糖果雨
 3     Author:        Chen Yang
 4     Time:        2012.5.25 9:29 pm
 5     State:        AC
 6     Memo:        二维树状数组、平行四边形变换统计
 7 */
 8 #include <cstdio>
 9 #include <cstdlib>
10 #include <algorithm>
11 #include <string>
12 #include <cstring>
13 using namespace std;
14 const int maxn=2020,maxm=4040,maxc=1000010;
15 int len,n,m,q;
16 int s[2][maxn][maxm],x[maxc],y[maxc];
17 
18 void add(int p,int x,int y,int v)
19 {
20     for (++x, ++y; x<maxn; x += x & -x)
21     for (int i=y; i<maxm; i += i & -i) s[p][x][i] += v;
22 }
23 
24 int sum(int p,int x,int y)
25 {
26     if (x<0 || y<0) return 0; int k = 0;
27     if (++x > n) x = n+1; if (++y > m) y = m+1;    
28     for (; x; x -= x & -x)
29     for (int i=y; i; i -= i & -i) k += s[p][x][i];
30     return k;
31 }
32 
33 inline void updata(int t,int c,int l,int r,int d)
34 {
35     x[c] = (t - l*d + n) % n, y[c] = r - l;
36     add(0,x[c],y[c]+x[c],1); add(1,x[c],y[c]-x[c]+n,1);
37 }
38 
39 inline void del(int c)
40 {
41     add(0,x[c],y[c]+x[c],-1); add(1,x[c],y[c]-x[c]+n,-1);
42 }
43 
44 inline int area(int p,int x1,int y1,int x2,int y2)
45 {
46     return sum(p,x2,y2)+sum(p,x1-1,y1-1)-sum(p,x1-1,y2)-sum(p,x2,y1-1);
47 }
48 
49 inline int ask(int t,int l,int r)
50 {
51     int d=r==len;
52     return area(0,t,l+t,t+r,m)+area(0,0,l+t-n,t+r-n-d,m)+
53            area(1,n-r+t+d,l-t,n,m)+area(1,t-r,l-t+n,t-1,m);
54 }
55 
56 int main()
57 {
58     freopen("candy.in","r",stdin);
59     freopen("candy.out","w",stdout);
60     scanf("%d%d", &q, &len); 
61     n = len << 1; m = len << 2;
62     for (int t,c,l,r,d,p; q--;)
63     {
64         scanf("%d%d", &p, &t);
65         if (p==1)
66         {
67             scanf("%d%d%d%d", &c, &l, &r, &d);
68             updata(t,c,l,r,d);
69         } 
70         else if (p==2)
71         {
72             scanf("%d%d", &l, &r);
73             printf("%d\n",ask(t%n,l,r));
74         } else
75         {
76             scanf("%d", &c);
77             del(c);
78         }
79     }
80 }
81             

 

posted @ 2012-05-26 09:00  datam  阅读(1173)  评论(0编辑  收藏  举报