【bzoj 2716】[Violet 3]天使玩偶 (cdq分治+樹狀陣列)
阿新 • • 發佈:2019-02-17
2716: [Violet 3]天使玩偶
Time Limit: 80 Sec Memory Limit: 128 MBSubmit: 1241 Solved: 546
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
100 10081 23
27 16
52 58
44 24
25 95
34 2
96 25
8 14
97 50
97 18
64 3
47 22
55 28
89 37
75 45
67 22
90 8
65 45
68 93
87 8
61 45
69 72
38 57
58 76
45 34
88 54
27 8
35 34
70 81
25 24
97 97
4 43
39 38
82 68
27 58
2 21
92 88
96 70
97 29
14 53
6 42
1 2
35 84
64 88
63 57
53 40
82 59
49 56
75 72
29 30
50 1
40 83
52 94
22 35
39 1
94 88
89 96
79 46
33 75
31 42
33 95
6 83
90 66
37 54
35 64
17 66
48 37
30 8
95 51
3 51
90 33
29 48
94 78
53 7
1 26
73 35
18 33
99 78
83 59
23 87
4 17
53 91
98 3
54 82
85 92
77 8
56 74
4 5
63 1
26 8
42 15
48 98
27 11
70 98
36 9
78 92
34 40
42 82
64 83
75 47
2 51 55
1 7 62
2 21 62
1 36 39
1 35 89
1 84 15
2 19 24
1 58 53
2 52 34
1 98 49
1 4 100
1 17 25
1 30 56
1 69 43
2 57 23
2 23 13
1 98 25
2 50 27
1 84 63
2 84 81
2 84 77
1 60 23
2 15 27
1 9 51
1 31 11
1 96 56
2 20 85
1 46 32
1 60 88
2 92 48
1 68 5
2 90 17
1 16 46
2 67 5
2 29 83
1 84 70
2 68 27
1 99 33
2 39 89
2 38 28
1 42 3
1 10 60
2 56 29
2 12 60
2 46 51
2 15 73
1 93 42
1 78 82
1 66 20
1 46 17
2 48 5
1 59 61
1 87 59
2 98 72
1 49 3
2 21 10
1 15 4
1 48 14
2 67 75
2 83 77
1 88 65
2 100 93
2 58 83
1 29 80
2 31 88
2 92 94
1 96 66
1 61 82
2 87 24
1 64 83
1 28 87
2 72 90
2 7 3
1 86 3
2 26 53
2 71 2
2 88 24
1 69 60
1 92 44
2 74 94
1 12 78
2 1 2
1 4 73
1 58 5
1 62 14
2 64 58
2 39 45
1 99 27
1 42 21
1 87 2
2 16 98
2 17 21
2 41 20
1 46 72
1 11 62
2 68 29
1 64 66
2 90 42
2 63 35
1 64 71
Sample Output
38
6
7
7
6
6
12
11
4
5
6
8
1
7
6
4
9
2
2
8
9
6
4
7
5
8
7
5
5
5
7
7
5
6
6
8
6
0
2
7
12
4
2
8
3
10
HINT
Source
【題解】【cdq分治】
【這道題,看看時限差點棄了。。。然而還好,卡過了】
【上來覺得,和Mokia差不多,只是把求和改成求最大值。實際也確實是這樣。】
【這道題考慮每個點的周圍四個方向上的點(左下、左上、右下、右上)時不能放在一起討論,不然就沒辦法分治了。所以每個點每次只考慮一個方向,做四次(趕腳時間會很可怕啊。。。)d=|x-x'|+|y-y'|,當在左下時,d=x+y-(x'+y'),這時只需查詢(x'+y')最大即可,依然以x為關鍵字排序,以y為下標維護樹狀陣列,每次查詢最大值。 由於左下位置是最好考慮的,那麼我們考慮把另三個方向通過軸對稱變換改變其與當前點的相對位置,將它們搞到左下來再處理】
【其實這道題最大的問題在於如何優化時間,如果按照上兩道題的方法,每次分治到一個區間就重新把左右區間排序,那麼時間上肯定不允許,畢竟按照這種方法,在每次改變點的相對位置後,還需要把操作的順序歸為初始,這樣就坐等TLE吧。。。 實際上,我們可以每次分治前,把整個操作序列按x為關鍵字排序,然後再分治到每一區間時,調整這個區間的操作順序,這樣就可以縮短時間】
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct slove{ int x,y,num,nm; int opt; }a[1000010],p[1000010]; int ans[1000010],tree[1000010]; int n,m,cnt,mod,tot; int tmp(slove a,slove b) { return a.x<b.x||a.x==b.x&&a.opt<b.opt; } int cmp(slove a,slove b) { return a.num<b.num; } inline int lowbit(int x) { return x&(-x); } inline void add(int x,int val) { while(x<=mod) { if(tree[x]<val) tree[x]=val; x+=lowbit(x); } return; } inline int ask(int x) { int ss=0; while(x) { if(ss<tree[x]) ss=tree[x]; x-=lowbit(x); } return ss; } inline void clear(int x,int val) { while(x<=mod) { tree[x]=val; x+=lowbit(x); } } void cdq(int l,int r) { if(l==r) return; int mid=(l+r)>>1; int tl=l,tr=mid+1; for(int i=l;i<=r;++i) if(a[i].num<=mid) p[tl++]=a[i]; else p[tr++]=a[i]; for(int i=l;i<=r;++i) a[i]=p[i]; int j=l; for(int i=mid+1;i<=r;++i) if(a[i].opt==2) { while(j<=mid&&a[j].x<=a[i].x) { if(a[j].opt==1) add(a[j].y,a[j].x+a[j].y); ++j; } int t=ask(a[i].y); if(t) { t=a[i].x+a[i].y-t; if(t<ans[a[i].nm]) ans[a[i].nm]=t; } } for(int i=l;i<j;++i) if(a[i].opt==1) clear(a[i].y,0); cdq(l,mid); cdq(mid+1,r); } int main() { int i; scanf("%d%d",&n,&m); memset(ans,127,sizeof(ans)); for(i=1;i<=n;++i) { ++cnt; int xx,yy; scanf("%d%d",&xx,&yy); a[cnt].x=++xx; a[cnt].y=++yy; a[cnt].opt=1; a[cnt].num=cnt; if(yy>mod) mod=yy; if(xx>mod) mod=xx; } for(i=1;i<=m;++i) { ++cnt; int xx,yy; scanf("%d%d%d",&a[cnt].opt,&xx,&yy); a[cnt].x=++xx; a[cnt].y=++yy; a[cnt].num=cnt; if(a[cnt].opt==2) a[cnt].nm=++tot; if(yy>mod) mod=yy; if(xx>mod) mod=xx; } mod++; sort(a+1,a+cnt+1,tmp); cdq(1,cnt);//查詢左下 for(i=1;i<=cnt;++i) a[i].x=-a[i].x+mod; sort(a+1,a+cnt+1,tmp); cdq(1,cnt);//查詢左上 for(i=1;i<=cnt;++i) a[i].y=-a[i].y+mod; sort(a+1,a+cnt+1,tmp); cdq(1,cnt);//查詢右下 for(i=1;i<=cnt;++i) a[i].x=-(a[i].x-mod); sort(a+1,a+cnt+1,tmp); cdq(1,cnt);//查詢右上 for(i=1;i<=tot;++i) printf("%d\n",ans[i]); return 0; }