poj2886Who Gets the Most Candies?

链接:http://poj.org/problem?id=2886

题意:给定n,k,然后给定n个坐成环的人的信息:名字和a[i]。第一次先删掉第k个人,然后从这个被删除的人开始数a[i],继续删下一个人,如果a[i]<0向前数,a[i]>0向后数。第i个删掉的人有一个值F[i],F[i]为i的约数个数。最后输出F[i]最大的人的名字和F[i]。如果有多个F[i]最大输出i最小的那个。

分析:首先我们将题目分解成两问,求F[i]和求最大F[i]时删除的人。F[i]为i的约数个数,这个我们可以用O(n)筛法筛素数的时候同时处理出来F[i],因为我们在用O(n)筛法筛x时,如果x为质数显然F[x]=2,如果x为合数那么我们是用x的最小质因子p去筛x的,那么有F[x]=2*F[x/p]并且如果(x/p)%p==0那么F[x]-=F[x/p/p],这个推导方法是将p看成特殊元素去用组合数学求解的,这里不加以详细分析。然后第二问就是求最大的F[i]是删除的是谁了,这是经典的约瑟夫环问题,我们用线段树维护维护就可以了。O(nlogn)。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=500010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const ll INF=10000000010;
typedef double db;
typedef unsigned long long ull;
char s[N][12];
int n,a[N],q[N],p[N],f[N],sum[N];
void deal(int n) {
    int i,j,k=0;
    f[1]=q[1]=1;
    for (i=2;i<=n;i++) {
        if (!q[i]) { f[i]=2;a[++k]=i; }
        for (j=1;j<=k;j++) {
            if (a[j]*i>n) break ;
            q[a[j]*i]=1;f[a[j]*i]=2*f[i];
            if (i%a[j]==0) {
                f[a[j]*i]-=f[i/a[j]];break ;
            }
        }
    }
}
void build(int x,int l,int r) {
    if (l==r) { sum[x]=1;return ; }
    int mid=(l+r)>>1;
    build(2*x,l,mid);build(2*x+1,mid+1,r);
    sum[x]=sum[2*x]+sum[2*x+1];
}
int query(int x,int l,int r,int k) {
    if (l==r) return l;
    int mid=(l+r)>>1;
    if (sum[2*x]>=k) return query(2*x,l,mid,k);
    return query(2*x+1,mid+1,r,k-sum[2*x]);
}
void updata(int x,int l,int r,int k) {
    if (l==r) { sum[x]=0;return ; }
    int mid=(l+r)>>1;
    if (sum[2*x]>=k) updata(2*x,l,mid,k);
    else updata(2*x+1,mid+1,r,k-sum[2*x]);
    sum[x]=sum[2*x]+sum[2*x+1];
}
void add(int a,int b) {
    for (;a<=n;a+=a&(-a)) p[a]+=b;
}
int getsum(int a) {
    int ret=0;
    for (;a;a-=a&(-a)) ret+=p[a];
    return ret;
}
int main()
{
    int i,m,k,w,mx,ans;
    deal(500000);
    memset(p,0,sizeof(p));
    while (scanf("%d%d", &n, &k)!=EOF) {
        for (i=1;i<=n;i++) scanf("%s%d", s[i], &a[i]);
        ans=mx=0;m=n;build(1,1,n);
        for (i=1;i<=n;i++) add(i,1);
        for (i=1;i<=n;i++) {
            w=query(1,1,n,k);updata(1,1,n,k);
            if (f[i]>mx) { ans=w;mx=f[i]; }
            add(w,-1);
            if (a[w]>0) k=getsum(w)+a[w];
            else k=getsum(w)+a[w]+1;
            if (i==n) break ;
            m--;k=((k-1)%m+m)%m+1;
        }
        printf("%s %d\n", s[ans], mx);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值