您的位置:首页 > 其它

线段树专辑 —— pku 3145 Harmony Forever

2011-11-14 20:06 369 查看
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

#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
#define inf 500005

struct node
{
int l;
int r;
int min_val;
};

node tree[2500000];
int n,len;
int pos[inf];
int val[inf];

int min(int a,int b)
{
return a<b?a:b;
}

void build(int i,int l,int r)
{
tree[i].l=l;
tree[i].r=r;
tree[i].min_val=inf;
if(l==r)
return;
int mid=(l+r)/2;
build(2*i,l,mid);
build(2*i+1,mid+1,r);
}

void updata(int i,int w) //更新区间[l,r]
{
if(tree[i].l>w || tree[i].r<w)
return;
if(tree[i].l>=w && tree[i].r<=w)
{
tree[i].min_val=w;
return;
}
updata(2*i,w);
updata(2*i+1,w);
tree[i].min_val=min(tree[2*i].min_val,tree[2*i+1].min_val);
}

int find(int i,int l,int r) //返回区间[l,r]的最小值
{
if(tree[i].l>r || tree[i].r<l)
return inf;
if(tree[i].l>=l && tree[i].r<=r)
return tree[i].min_val;
if(tree[i].l<tree[i].r)
{
return min(find(2*i,l,r),find(2*i+1,l,r));
}
return inf;
}

int fun(int y) //直接查找
{
int i,ans=inf,k;
for(i=len;i>=1;i--)
{
if(val[i]%y==0)
{
return i;
}
if(val[i]%y<ans)
{
ans=val[i]%y;
k=i;
}
}
return k;
}

int solve(int mod)
{
int l=0,r=mod-1,ans=inf,k,temp;//[l,r]便是鸽笼的大小
while(l<=500000)
{
if(r>500000)
r=500000;
temp=find(1,l,r);
if(temp!=inf && temp%mod<ans)
{
ans=temp%mod;
k=pos[temp];
}
else if(temp%mod==ans && pos[temp]>k)
{
k=pos[temp];
}
l+=mod;
r+=mod;
}
return k;
}

int main()
{
char c;
int x,cas=1,i,ans;
//freopen("d:\\in.txt","r",stdin);
while(scanf("%d",&n)!=EOF && n)
{
if(cas>1)
printf("\n");
build(1,0,500000);
printf("Case %d:\n",cas++);
len=0;
for(i=0;i<n;i++)
{
getchar();
scanf("%c %d",&c,&x);
if(c=='B')
{
len++;
pos[x]=len;
val[len]=x;
updata(1,x);
}
else
{
if(len==0)
{
printf("-1\n");
continue;
}
if(x<=5000)//范围较小的时候直接查找
{
ans=fun(x);
printf("%d\n",ans);
}
else
{
ans=solve(x);
printf("%d\n",ans);
}
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: