题目描述
糖果雨 【问题描述】 有一个美丽的童话:在天空的尽头有一个"糖果国",这里大到摩天大厦,小 到小花小草都是用糖果建造而成的。更加神奇的是,天空中飘满了五颜六色的糖 果云,很快糖果雨密密麻麻从天而落,红色的是草莓糖,黄色的是柠檬糖,绿色 的是薄荷糖, 黑色的是巧克力糖......这时糖果国的小朋友们便会拿出大大小小的 口袋来接天空中落下的糖果,拿回去与朋友们一起分享。 对糖果情有独钟的小 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 = -1 或 1。 数据保证任何时刻空中不会出现两片颜色相同的云朵。时刻 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 xdo 23 begin 24 i:=y; 25 while i 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 #include9 #include 10 #include 11 #include <string> 12 #include 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 x) 21 for (int i=y; 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