线段树专辑 —— pku 3145 Harmony Forever

http://poj.org/problem?id=3145

Harmony Forever。。。多吹牛B啊。

这题巧妙的运用了鸽笼原理,什么是鸽笼原理?鸽笼原理就是给你N+1个数,则必定至少有两个数的余数是相同的!

解决这题的时候,我们先用RMQ的方法求出每一段的最小值,这是容易做到的。

这题的数据范围是500000,也就是说最多会有500000个叶子节点,当该叶子节点的值为inf的时候,表示这里没有值。当它的值等于它的位置的时候,表示它有值,例如:

集合[3,4,5]在线段树中叶子节点中的表示为 [inf,inf,inf,3,4,5],前面三个inf分别表示0位,1位和2位

询问的时候,假设我们要求的MOD是Y,则首先查找[0,Y-1]区间的最小值,因为这样的区间不会有两个数的余数相同,记录下结果。然后依次查找[Y,Y+Y-1],[Y+Y,Y+Y+Y-1]。。。。等区间,每个区间都会找出一个最小值,我们将这些最小值对Y进行取模,得到最小值!

还是看上面的例子,假如现在MOD是4.

根据上面的方法,我们便先查找[0,3],得到最小值是3,3%4==3,这是目前的答案。

接下来查找区间[4,5],得到最小值4,4%4==0,这便是最新的答案!

从这个例子中,我们也可以看到鸽笼原理的正确性,不过不对的话,那么假如我们首先查找区间[0,4],那么最小值是3,再查找区间[5,5],最小值是5,这样的话,最优的4就被我们丢了!

关于鸽笼原理更多知识,看组合数学吧!

View Code
  1 #include<iostream>
2 #include<string>
3 #include<cmath>
4 #include<algorithm>
5 using namespace std;
6 #define inf 500005
7
8 struct node
9 {
10 int l;
11 int r;
12 int min_val;
13 };
14
15 node tree[2500000];
16 int n,len;
17 int pos[inf];
18 int val[inf];
19
20 int min(int a,int b)
21 {
22 return a<b?a:b;
23 }
24
25 void build(int i,int l,int r)
26 {
27 tree[i].l=l;
28 tree[i].r=r;
29 tree[i].min_val=inf;
30 if(l==r)
31 return;
32 int mid=(l+r)/2;
33 build(2*i,l,mid);
34 build(2*i+1,mid+1,r);
35 }
36
37 void updata(int i,int w) //更新区间[l,r]
38 {
39 if(tree[i].l>w || tree[i].r<w)
40 return;
41 if(tree[i].l>=w && tree[i].r<=w)
42 {
43 tree[i].min_val=w;
44 return;
45 }
46 updata(2*i,w);
47 updata(2*i+1,w);
48 tree[i].min_val=min(tree[2*i].min_val,tree[2*i+1].min_val);
49 }
50
51 int find(int i,int l,int r) //返回区间[l,r]的最小值
52 {
53 if(tree[i].l>r || tree[i].r<l)
54 return inf;
55 if(tree[i].l>=l && tree[i].r<=r)
56 return tree[i].min_val;
57 if(tree[i].l<tree[i].r)
58 {
59 return min(find(2*i,l,r),find(2*i+1,l,r));
60 }
61 return inf;
62 }
63
64 int fun(int y) //直接查找
65 {
66 int i,ans=inf,k;
67 for(i=len;i>=1;i--)
68 {
69 if(val[i]%y==0)
70 {
71 return i;
72 }
73 if(val[i]%y<ans)
74 {
75 ans=val[i]%y;
76 k=i;
77 }
78 }
79 return k;
80 }
81
82 int solve(int mod)
83 {
84 int l=0,r=mod-1,ans=inf,k,temp;//[l,r]便是鸽笼的大小
85 while(l<=500000)
86 {
87 if(r>500000)
88 r=500000;
89 temp=find(1,l,r);
90 if(temp!=inf && temp%mod<ans)
91 {
92 ans=temp%mod;
93 k=pos[temp];
94 }
95 else if(temp%mod==ans && pos[temp]>k)
96 {
97 k=pos[temp];
98 }
99 l+=mod;
100 r+=mod;
101 }
102 return k;
103 }
104
105 int main()
106 {
107 char c;
108 int x,cas=1,i,ans;
109 //freopen("d:\\in.txt","r",stdin);
110 while(scanf("%d",&n)!=EOF && n)
111 {
112 if(cas>1)
113 printf("\n");
114 build(1,0,500000);
115 printf("Case %d:\n",cas++);
116 len=0;
117 for(i=0;i<n;i++)
118 {
119 getchar();
120 scanf("%c %d",&c,&x);
121 if(c=='B')
122 {
123 len++;
124 pos[x]=len;
125 val[len]=x;
126 updata(1,x);
127 }
128 else
129 {
130 if(len==0)
131 {
132 printf("-1\n");
133 continue;
134 }
135 if(x<=5000)//范围较小的时候直接查找
136 {
137 ans=fun(x);
138 printf("%d\n",ans);
139 }
140 else
141 {
142 ans=solve(x);
143 printf("%d\n",ans);
144 }
145 }
146 }
147 }
148 return 0;
149 }



原文地址:https://www.cnblogs.com/ka200812/p/2248853.html