1. 程式人生 > >JZOJ-senior-2439. 【BJOI2011】元素 (Standard IO)

JZOJ-senior-2439. 【BJOI2011】元素 (Standard IO)

Time Limits: 2000 ms Memory Limits: 262144 KB Detailed Limits

Description

相傳,在遠古時期,位於西方大陸的 Magic Land 上,人們已經掌握了用魔法礦石煉製法杖的技術。那時人們就認識到,一個法杖的法力取決於使用的礦石。 一般地,礦石越多則法力越強,但物極必反:有時,人們為了獲取更強的法力而使用了很多礦石,卻在煉製過程中發現魔法礦石全部消失了,從而無法煉製出法杖,這個現象被稱為“魔法抵消” 。特別地,如果在煉製過程中使用超過一塊同一種礦石,那麼一定會發生“魔法抵消”。

後來,隨著人們認知水平的提高,這個現象得到了很好的解釋。經過了大量的實驗後,著名法師 Dmitri 發現:如果給現在發現的每一種礦石進行合理的編號(編號為正整數,稱為該礦石的元素序號),那麼,一個礦石組合會產生“魔法抵消”當且僅當存在一個非空子集,那些礦石的元素序號按位異或起來為零。 (如果你不清楚什麼是異或,請參見下一頁的名詞解釋。 )例如,使用兩個同樣的礦石必將發生“魔法抵消”,因為這兩種礦石的元素序號相同,異或起來為零。

並且人們有了測定魔力的有效途徑,已經知道了:合成出來的法杖的魔力等於每一種礦石的法力之和。人們已經測定了現今發現的所有礦石的法力值,並且通過實驗推算出每一種礦石的元素序號。

現在,給定你以上的礦石資訊,請你來計算一下當時可以煉製出的法杖最多有多大的魔力。

Input

第一行包含一個正整數N,表示礦石的種類數。 接下來 N行,每行兩個正整數Numberi 和 Magici,表示這種礦石的元素序號和魔力值。

Output

僅包含一行,一個整數:最大的魔力值。

Sample Input

【Case 1】 3 1 10 2 20 3 30

【Case 2】 7 5 5 3 20 9 40 10 30 5 10 6 10 5 10

Sample Output

【Case 1】 50

【Case 2】 80

Hint

【樣例 1 說明】 由於有“魔法抵消”這一事實,每一種礦石最多使用一塊。 如果使用全部三種礦石,由於三者的元素序號異或起來:1 xor 2 xor 3 = 0 , 則會發生魔法抵消,得不到法杖。 可以發現,最佳方案是選擇後兩種礦石,法力為 20+30=50。 【樣例 2 說明】 一個最佳方案是:選擇第 3、4、5 種礦石。 注意:可能有一些礦石,它們有相同的元素序號。 【資料規模】 對於 30%的資料:N ≤ 10; 另外有 40%的資料:Numberi 在二進位制表示下恰好有兩位是1,如樣例 2; 對於全部的資料:N ≤ 1000,Numberi ≤ 1

01810^{18},Magici ≤ 10410^4。 【名詞解釋】 異或(exclusive disjunction)是一種邏輯運算,滿足: 0 xor 0 = 0 0 xor 1 = 1 1 xor 0 = 1 1 xor 1 = 0 按位異或即:將兩自然數在二進位制下表示,每一位進行以上的運算。 在 C/C++中,該操作對應為 “^” ;在 Pascal中,該操作對應為“xor”。 容易發現,自然數集中的該運算構成 Abel群(滿足交換律、結合律)

Solution

線性基+貪心

我們需要用線性基來維護我們選取的非空子集中不存在異或出結果為0的情況 然後還需要滿足最後得到的權值最大 根據異或的性質我們不難想到可以根據貪心來求解 我們將每件物品按照權值從大到小排序,然後不斷插入線性基中累加答案即可

Code

#include<algorithm>
#include<cstdio>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define ll long long

using namespace std;

const int N=1e3+5;
int n;
ll ans,p[63];

struct node
{
	ll num;
	int val;
}a[N];

bool cmp(node x,node y)
{
	return x.val>y.val;
}

int main()
{
	scanf("%d",&n);
	fo(i,1,n) scanf("%lld%d",&a[i].num,&a[i].val);
	sort(a+1,a+1+n,cmp);
	fo(i,1,n)
	{
		fd(j,62,0)
			if((a[i].num>>j)&1)
			{
				if(!p[j])
				{
					p[j]=a[i].num;
					break;
				}
				a[i].num^=p[j];
			}
		if(a[i].num) ans+=a[i].val;
	}
	printf("%lld",ans);
}