POJ 2886 线段树

来源:互联网 发布:网络女主播真实收入 编辑:程序博客网 时间:2024/06/05 02:45

该题和poj2828的思路非常类似。

题意:

有N个人参加一个游戏,每个人手上都拿了一张卡片,每张卡片的数字都是非0的,现在从第K人开始,第K人离开后,这时候根据第K人手上拿着的卡片的数字,决定下一个人是谁,如果这个数字m为正数,那么其实相当于向右数m个,如果这个数字为负数,那么其实相当于向左数m个数的位置(所有的数法都是要排除自己)。

现在构建一颗类似于poj2828的线段树,这时候主要的关键是快速的定位下一个数该是哪一个空位。如果这个数是正数y,那么当前位置K前面的数有K-1个,即前面有K-1个数,这时候为防止取模后等于0,先减去1,最后取模完成后再加上1,这时候加上y,就是下一个数的位置(注意这里说的位置说的是第几个空位),但是你看在代码里面可能发现有些与上面的公式不同,因为K-2有可能是负数,但是空位 不可能是负数,所以加上剩下的数的总数已达到正数的目的。然后

接下来说下负数,负数我们可以这样思考,因为正数相当于向前走,那么负数就是说要倒着数,和正数一样,先减去1,然后取模再加上1。

#include<iostream>#include<fstream>#include<string>#include<cmath>#include<cstring>using namespace std;#define Lchild rt<<1,L,m#define Rchild rt<<1|1,m+1,R#define maxn 500100int tree[maxn * 3];int ans[maxn];//ofstream fout("fout.txt");char name[maxn][20];int inte[maxn];int N;void build(int rt=1,int L=1,int R=N){tree[rt] = R - L + 1;if (L == R)return;int m = (L + R) >> 1;build(Lchild);build(Rchild);}int query(int pos,int rt=1,int L=1,int R=N){tree[rt]--;if (L == R){return L;}int m = (L + R) >> 1;int ret;if (tree[rt << 1] >= pos)ret=query(pos, Lchild);else{pos -= tree[rt << 1];ret=query(pos, Rchild);}return ret;}void init(){int s = (int)sqrt(maxn*1.0);for (int i = 1; i <= s; i++){for (int j = i + 1; j*i <= maxn; j++)ans[j*i] += 2;ans[i*i]++;}}int main(){int K;init();while (scanf_s("%d%d",&N,&K)!=EOF){build();for (int i = 1; i <= N; i++){cin >> name[i];scanf_s("%d", &inte[i]);}int ret;int ansstr;int ansnum=0;int t = 0;while (tree[1]){int pos = K;ret = query(pos);t++;pos = inte[ret];if (ans[t] > ansnum){ansstr = ret;ansnum = ans[t];}if (!tree[1])break;if (pos > 0){pos = ((K - 2 + tree[1]) % tree[1]+pos)%tree[1]+1;}else{pos = ((K + pos - 1) % tree[1]+tree[1]) % tree[1] + 1;}K = pos;}cout << name[ansstr]<< " " << ansnum << endl;}return 0;}

1 0
原创粉丝点击