程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> poj 2886 Who Gets the Most Candies?(線段樹單點更新模擬約瑟夫環)

poj 2886 Who Gets the Most Candies?(線段樹單點更新模擬約瑟夫環)

編輯:C++入門知識

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

題意:N 個小孩圍成一圈,他們被順時針編號為 1 到 N。每個小孩手中有一個卡片,上面有一個非 0 的數字,游戲從第 K 個小孩開始,他告訴其他小孩他卡片上的數字並離開這個圈,他卡片上的數字 A 表明了下一個離開的小孩,如果 A 是大於 0 的,則下個離開的是左手邊第 A 個,如果是小於 0 的,則是右手邊的第 -A 個小孩。游戲將直到所有小孩都離開,在游戲中,第 p 個離開的小孩將得到 F(p) 個糖果,F(p) 是 p 的約數的個數,問誰將得到最多的糖果。輸出最幸運的小孩的名字和他可以得到的糖果。

思路:乍一看這個題真不知道用線段樹怎麼寫,類似於約瑟夫環問題,每次從圈中刪除一個人,直到所有人出圈。其實線段樹恰好可以解決從圈中刪除一人這個問題,即單點更新。

因為n個小孩依次出圈,要知道誰的糖果最多,也就是求誰出圈的次序號的約數最多。所以我們可以預處理,先求出1~n中約數最多的那個數maxid,其約數個數是maxnum。所以我們直接模擬約瑟夫環,誰的出圈序號是maxid,誰得到的糖果最多。

當第一個人出圈後,下一個怎麼求?這就是由相對位置求原始位置的問題。只有先求出這次出圈的人的原始位置才能求出下一個人。這是通過線段樹來求的。線段樹附加區域存的是這個區間還有多少人,每踢出一個人區間的長度減1.求原始位置的方法與poj2828插隊問題類似。只不過那個是求出原始位置後直接插入,而這個是返回原始位置。


#include 
#include 

const int maxn = 500005;

struct line
{
	int l;
	int r;
	int len;
}tree[maxn<<2];

int n,k;
int maxid;	//約束最大的編號
int maxnum;	//約束個數的最大值,即maxid的約束個數
int div[maxn];//預處理每個數的約束個數

//預處理約數個數,並找出約束個數最多的編號maxid及其約束個數maxnum
void solve_division()
{
	memset(div,0,sizeof(div));
	for(int i = 1; i <= n; i++)
	{
		div[i]++;
		for(int j = i*2; j <= n; j+=i)
			div[j]++;
	}
	maxid = 1;
	maxnum = div[1];
	for(int i = 2; i <= n; i++)
	{
		if(div[i] > maxnum)
		{
			maxnum = div[i];
			maxid = i;
		}
	}
}

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].len = r-l+1;
	if(l == r)
		return ;
	int mid = (l+r)>>1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
}

int query(int v, int k)	//由相對位置找到其實際位置
{
	tree[v].len--;
	if(tree[v].l == tree[v].r)
		return tree[v].l;

	if(k <= tree[v*2].len)
		return query(v*2,k);
	else return query(v*2+1,k-tree[v*2].len);
}

char name[maxn][15];
int card[maxn];

int main()
{
	while(~scanf("%d %d",&n,&k))
	{
		solve_division();
		for(int i = 1; i <= n; i++)
			scanf("%s %d",name[i],&card[i]);

		build(1,1,n);

		int pos;
		for(int i = 0; i < maxid; i++)	//出圈maxid即可,就能找出第maxid次出圈的人的實際位置
		{
			pos = query(1,k);//找到原始位置
			n--;//出圈,個數減1
			if(n==0) break;
			if(card[pos] > 0)
				k=(k-1+card[pos]-1)%n+1;
			else
				k=((k-1+card[pos])%n+n)%n+1;
		}
		printf("%s %d\n",name[pos],maxnum);
	}
	return 0;
}


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved