您的位置:首页 > 其它

poj2886 Who Gets the Most Candies?

2016-07-17 20:48 393 查看
题意:输入n,k表示n个人,第k个第一个离开圆环,之后n行,每行一个名字和一个数字m,表示从这个人开始顺时针(数字大于0)或逆时针(数字小于0)第m个人离开圆环。每个人离开时会有一个得分,得分=F(离开次序i); F(i)=能整除i的数的个数;求这个人的名字和得分。

思路:首先打表枚举[1,500000]的F(i);  然后模拟求这个人的名字,对于[1,n]中,求出是F(i)最大的i值,模拟i次离开就行了。人数比较多,直接模拟肯定不行,需要借助线段树(树状数组)直接计算出要离开圆环的人的坐标,线段树中维护的是这个区间中还剩下几个人。

很多人都把反素数表打了出来,我直接在代码中计算的,只会耗费大约300MS。

我自己写的(2700MS):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<climits>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#define maxn 510000

using namespace std;

struct node
{
char name[50];
int next;
int score;
}tp[maxn];
int num[maxn];
struct tree
{
int l,r;
int sum;
}tr[maxn*3];

void update(int o,int l,int r,int pos,int v)
{
int m=(l+r)/2;
if(l==r)
{
tr[o].sum=v;
}
else
{
if(pos<=m)
update(o*2,l,m,pos,v);
else
update(o*2+1,m+1,r,pos,v);
tr[o].sum=tr[o*2].sum+tr[o*2+1].sum;
}
}
int query(int o,int l,int r ,int ql,int qr)
{
int m=(l+r)/2;
if(ql>qr)
return 0;
if(ql<=l && r<=qr)
return tr[o].sum;
int ans=0;
if(ql<=m)
ans+=query(o*2,l,m,ql,qr);
if(m<qr)
ans+=query(o*2+1,m+1,r,ql,qr);
return ans;
}
void calcu()
{
memset(num,0,sizeof(num));
for(int i=1;i<maxn;++i)
{
for(int j=1 ;j*i<maxn;++j)
{
num[i*j]++;
}
}
}
int _find(int o,int l,int r,int pos)
{
if(l==r)
return l;
int m=(l+r)/2;
if(tr[o*2].sum>=pos)
return _find(o*2,l,m,pos);
else
return _find(o*2+1,m+1,r,pos-tr[o*2].sum);
}
int main()
{
int n,k;
calcu();
while(scanf("%d%d",&n ,&k)!=EOF)
{
int mmax=0,pmax;
for(int i=1;i<=n;++i)
{
if(num[i]>mmax)
{
mmax=num[i];
pmax=i;
}
}
memset(tr,0,sizeof(tr));
for(int i=0;i<n;++i)
{
scanf("%s%d",tp[i].name,&tp[i].next);
update(1,0,n-1,i,1);
}
int p=1;
int re=query(1,0,n-1,0,n-1);
int ans_k;
while( p<=pmax)
{
re-=1;
tp[k-1].score=num[p++];
ans_k=k-1;
int mov=tp[k-1].next;
if(mov>=0)
{
if(re!=0)
mov=mov%re;
if(mov==0)
mov=re;
}
else
{
if(re!=0)
mov=-(abs(mov)%re);
mov+=re+1;
if(re!=0)
mov%=re;
if(!mov)
mov=re;
}
update(1,0,n-1,k-1,0);
int re_l=query(1,0,n-1,0,k-1);
int re_r=query(1,0,n-1,k,n-1);
if(mov>re_r)
{
mov-=re_r;
}
else
{
mov+=re_l;
}
k=_find(1,0,n-1,mov)+1;
}
cout<<tp[ans_k].name<<" "<<tp[ans_k].score<<endl;
}
}


看了别人写的后改进的(1700MS,把反素数表打出来还能再节省300MS):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<climits>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#define maxn 510000

using namespace std;

struct node
{
char name[50];
int next;
int score;
}tp[maxn];
int num[maxn];
struct tree
{
int l,r;
int sum;
}tr[maxn*3];

void ini(int o,int l,int r)
{
if(l==r)
{
tr[o].sum=1;
return ;
}
if(l>r)
return ;
tr[o].sum=(r-l)+1;
int m=(l+r)/2;
ini(o*2,l,m);
ini(o*2+1,m+1,r);
}
void calcu()
{
memset(num,0,sizeof(num));
for(int i=1;i<maxn;++i)
{
for(int j=1 ;j*i<maxn;++j)
{
num[i*j]++;
}
}
}
int _find(int o,int l,int r,int pos)
{
tr[o].sum--;
if(l==r)
return l;
int m=(l+r)/2;
if(tr[o*2].sum>=pos)
return _find(o*2,l,m,pos);
else
return _find(o*2+1,m+1,r,pos-tr[o*2].sum);
}
int main()
{
int n,k;
calcu();
while(scanf("%d%d",&n ,&k)!=EOF)
{
int mmax=0,pmax;
for(int i=1;i<=n;++i)
{
if(num[i]>mmax)
{
mmax=num[i];
pmax=i;
}
}
memset(tr,0,sizeof(tr));
for(int i=0;i<n;++i)
{
scanf("%s%d",tp[i].name,&tp[i].next);
}
ini(1,0,n-1);
int p=1;
int re=n;
int ans_pos,pos;
while( p<=pmax)
{
re-=1;
pos=_find(1,0,n-1,k)+1;
ans_pos=pos-1;
tp[pos-1].score=num[p++];
if(re==0)
break;
int mov=tp[pos-1].next;
if(mov>=0)
{
mov=(k-1+mov)%re;
if(mov==0)
mov=re;
}
else
{
mov=-(abs(mov)%re);
mov+=re+1;
mov=(mov+k-1)%re;
if(mov==0)
mov=re;
}
k=mov;
}
cout<<tp[ans_pos].name<<" "<<tp[ans_pos].score<<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj poj2886